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:
{ "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:
{
"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:
{ "detail": "API key not found." }Status codes
| Status | Meaning |
|---|---|
200 | The request succeeded. |
401 | The credential was missing or not accepted. One body for every refusal cause; see Authentication. |
403 | The action is not available to your workspace. Creating a key without API access enabled answers 403; see Keys and access management. |
404 | The 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. |
405 | The method is not supported. /v1 endpoints accept GET only. |
422 | The request failed validation. The body names the parameter or field. |
429 | A traffic ceiling was reached; see below. |
503 | The 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:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The per-minute ceiling for this key. |
X-RateLimit-Remaining | Requests left in the current window. |
X-RateLimit-Reset | When 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.
{
"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.
{
"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.
{
"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-Remainingrather than reacting to refusals.