REST API
Programmatic access to your NapMap account — create recommendations from your CMS, attach links to existing articles, pull earnings into your analytics, all over HTTPS with bearer-token auth.
Authentication
Every endpoint expects a bearer token in the Authorization header. Tokens are managed at /profile/tokens — you assign scopes when creating one and copy the value once (we only store its SHA-256 hash).
Authorization: Bearer your_token_here
Tokens carry one or more scopes:
read-links— list and read your short linkswrite-links— create and delete linksread-articles— list and read your articleswrite-articles— create and update articlesread-earnings— query earnings summary and daily breakdownread-clicks— read recent click records
Base URL
https://napmap.net/api/v1
Rate limits
Two layers:
- IP-based outer ring: 300 requests / minute / IP — catches unauthenticated probing and token stuffing.
- Per-token bucket: 60 requests / minute / token by default (admin-configurable). Responses carry
X-RateLimit-LimitandX-RateLimit-Remaining; 429s includeRetry-After.
Endpoints
GET /me
Returns the account that owns the bearer token. No scope required.
curl -H "Authorization: Bearer $TOKEN" \
https://napmap.net/api/v1/me
Articles
GET /articles — list your articles (paginated, per_page 1–100). Requires read-articles.
GET /articles/{id} — fetch one. Requires read-articles.
POST /articles — create a new article. Requires write-articles.
curl -X POST https://napmap.net/api/v1/articles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"category_id": 1,
"title": "5 tools I actually used this quarter",
"excerpt": "What survived the cull and why.",
"content": "## The shortlist\n\nThree calendars and two notes apps walked in. One came out…",
"language": "en",
"publish": true
}'
Body must be at least articles.min_content_length chars (default 200). Articles run through duplicate detection (SimHash) against your other articles — near-duplicates return 422 with error: "duplicate" and the closest match.
PATCH /articles/{id} — partial update. Same payload fields are accepted (all optional). Requires write-articles.
Links
GET /links — list (paginated). Requires read-links.
GET /links/{id} — fetch one. Requires read-links.
POST /links — create a link. Requires write-links.
Two modes:
- Standalone — pass
destination_urlonly. The link is created without an article attached; readers hitting the short URL see a fallback. - Attach to existing article — pass
article_idof one of your articles. The link inherits the article's title + category. Each article can serve up to 20 links (cap).
curl -X POST https://napmap.net/api/v1/links \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://example.com/tool-x",
"article_id": 42
}'
Response includes the short URL ready to share:
{
"data": { "id": 1234, "short_code": "aBc12", "...": "..." },
"short_url": "https://napmap.net/aBc12",
"links_on_article": 3,
"cap": 20
}
DELETE /links/{id} — soft delete. Requires write-links.
Earnings
GET /earnings/summary — totals (balance / pending / lifetime). Requires read-earnings.
GET /earnings/daily — last 30 days as [{date, valid_clicks, earned}]. Requires read-earnings.
Clicks
GET /clicks — recent click records (paginated). Read-only. Requires read-clicks.
Errors
All error responses use JSON shape { "error": "code", "message": "..." } with the relevant HTTP status:
401 unauthorized— missing or invalid token, or token expired403 forbidden— token missing the scope needed for this endpoint404 not_found— resource doesn't exist or isn't owned by this account (we don't leak existence)422— validation failure (standard Laravel{ message, errors }shape) or domain-specific code:duplicate,blocked,cap_reached429 rate_limited— back off and retry afterRetry-Afterseconds
Versioning
We commit to /api/v1/* being backwards-compatible. Breaking changes will land at /api/v2/* with a deprecation window announced at least 90 days in advance.
Questions?
Email [email protected].