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:

401 Unauthorized
{
  "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:

422 Unprocessable Entity
{
  "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 after Retry-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/categories and the category filter 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.

curl
# 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"