API documentation

Errors and rate limits

Error bodies

Every 4xx refusal from a /v1 endpoint carries one structured error object with a stable machine-readable code:

json
{ "error": { "code": "not_found", "message": "Resource not found." } }

The code and its message are canonical per status code, never per call site, so a refusal does not reveal why a particular request was turned away. Codes served today: invalid_request (400), unauthenticated (401), forbidden (403), not_found (404), method_not_allowed (405), conflict (409), rate_limited (429).

Traffic refusals (the ceilings below) carry the same envelope plus a retry_after_seconds field:

json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after the number of seconds in Retry-After.",
    "retry_after_seconds": 21
  }
}

Branch on error.code, never on message text. Codes are stable: new codes may be added over time, existing codes are never renamed.

The management endpoints under /org/api-keys carry a single detail message instead:

json
{ "detail": "API key not found." }

Status codes

StatusMeaning
200The request succeeded.
401The credential was missing or not accepted. One body for every refusal cause; see Authentication.
403The action is not available to your workspace. Creating a key without API access enabled answers 403; see Keys and access management.
404The resource does not exist in your workspace. The API does not distinguish between a resource that does not exist and one that belongs to another workspace.
405The method is not supported. /v1 endpoints accept GET only.
422The request failed validation. The body names the parameter or field.
429A traffic ceiling was reached; see below.
503The request could not be served safely; retry later. See quota_unavailable below.

Rate limits

Two ceilings apply to every request an API key authenticates:

  • a per-key rate limit over a one-minute window, and
  • a per-workspace daily quota across all the workspace's keys, which resets at midnight UTC.

The limits themselves are part of your arrangement and are not fixed numbers in this documentation. The per-minute ceiling that applies to your key is visible in the response headers below.

Response headers

Every response to a request an API key authenticates carries the state of the minute window, traffic refusals included:

HeaderMeaning
X-RateLimit-LimitThe per-minute ceiling for this key.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetWhen the current window resets, as a Unix timestamp in seconds.

429 responses additionally carry a Retry-After header, in seconds.

429 with code rate_limited

The per-key rate limit was exceeded. The window is short; wait for Retry-After seconds and retry.

json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after the number of seconds in Retry-After.",
    "retry_after_seconds": 21
  }
}

429 with code daily_quota_exceeded

The workspace's daily quota was exhausted. Retry-After counts to midnight UTC, when the quota resets.

json
{
  "error": {
    "code": "daily_quota_exceeded",
    "message": "Daily quota exceeded. The quota resets at UTC midnight.",
    "retry_after_seconds": 14580
  }
}

503 with code quota_unavailable

Usage accounting could not be reached, so the request was refused rather than served unmetered. There is no Retry-After; retry with backoff.

json
{
  "error": {
    "code": "quota_unavailable",
    "message": "The daily quota could not be verified. The request was refused."
  }
}

Retry guidance

  • On 429, wait for the number of seconds in Retry-After, then retry.
  • On 503, retry with exponential backoff and a bounded number of attempts.
  • Pace steady workloads against X-RateLimit-Remaining rather than reacting to refusals.