Errors, rate limits & pagination
Predictable error codes, plan-level rate limits and quotas, and the pagination conventions shared by every list endpoint.
The API uses conventional HTTP status codes and returns a JSON body on every error. Read the status code first to decide whether to fix the request, back off, or retry.
#Error format
Errors carry a single detail field describing what went wrong:
{
"detail": "Missing API key. Provide X-API-Key header or Authorization: Bearer <key>"
}Validation errors (malformed or out-of-range parameters) return a structured list:
{
"detail": [
{
"loc": ["query", "granularity"],
"msg": "value is not a valid enumeration member; permitted: 'day', '3h'",
"type": "type_error.enum"
}
]
}#Status codes
200 OK— success; the body is the resource.400 Bad Request— a parameter is present but unusable.401 Unauthorized— missing or invalid API key. See Authentication.403 Forbidden— the key isn’t scoped to the requested market.404 Not Found— no resource with that id.422 Unprocessable Entity— a query parameter failed validation (wrong type, bad enum, out of range).429 Too Many Requests— you’ve exceeded the rate limit; back off and retry.503 Service Unavailable— a dependency is briefly degraded. Retry afterRetry-After; not a client error.
#Rate limits & quotas
The monthly request quota is set by the project’s plan. The REST API and the MCP server draw on the same quota:
- Start — 100 requests / month.
- Grow — 10,000 requests / month.
- Scale — 500,000 requests / month.
- Enterprise — 2,000,000 requests / month.
The quota grows with what the project buys on top of its plan: each extra tracked entity adds 10 requests / month, each extra keyword 2 and each extra tracked publisher 10.
Every plan also has a per-minute rate limit; a request over it returns 429. A project can hold up to 10 API keys, each independently scoped and revocable. Need more requests? Grow and Scale can add request packs: see the add-ons on the pricing page.
#Plan limits
When a request asks for something the project’s plan does not include, the error names the reason in a machine-readable code. These are not rate limits: do not retry them.
403 api_not_in_plan,403 mcp_not_in_plan— returned when API or MCP access is switched off for the project.403 category_not_in_plan— the request names a category outside the project’s chosen top-level categories (Start and Grow choose theirs; Scale and Enterprise have all).403 endpoint_not_in_plan_scope— the endpoint cannot be limited to the chosen categories, so it is closed to projects with a category limit. This covers/v1/analytics/categoriesand thecategoryfilter of/v1/articles.403 youtube_not_in_plan— a YouTube publisher or video, on a plan without YouTube in its data.404 out_of_plan_scope— the article exists but lies outside the chosen categories.402 subscription_inactive— the project’s subscription is not active.503 entitlement_unavailable,503 scope_unavailable— the plan could not be read; retry shortly. Unfiltered data is never returned instead.
History works differently: a window that reaches further back than the plan allows is moved forward, not refused, and the response says so with history_clamped.
#Pagination
List endpoints return a bounded page of results. Most accept limit (page size, with a sensible default and maximum) and offset (how many to skip); the v2 feed additionally supports cursor paging for stable, gap-free iteration. Each endpoint’s reference lists its exact paging parameters and defaults.
# page 2, 50 per page
curl "https://d2tr.com/api/v1/entities?region=us&limit=50&offset=50" \
-H "X-API-Key: $D2TR_API_KEY"