Errors
Every error body carries a stable machine-readable code alongside the human-readable error message. Branch on code, not on the message text or the HTTP status alone (one status can map to more than one code).
Two things are deliberately not errors: owning every item in a selection (a 200 with subset_exhausted: true on /items?exclude_owned=true) and re-downloading an item you already own (a 200 with charged: false). Neither returns an error code.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_param | An unknown value for a fixed dimension; the response gives param + allowed. |
| 400 | invalid_cursor | The cursor passed for keyset paging is malformed or expired. |
| 400 | ambiguous_benchmark | Benchmark omitted while the key sees more than one; the response lists available. |
| 400 | unsupported_filter | A filter combination the endpoint cannot serve (e.g. exclude_owned combined with an attribute-backed filter). |
| 401 | unauthorized | Missing, invalid, or revoked API key. Copy a current key from the portal. |
| 402 | insufficient_credits | Out of credits (live tier). Top up in the portal. |
| 403 | forbidden_tier | Key not permitted for this item (e.g. a test key requesting a live-tier item). |
| 403 | forbidden_scope | The item is outside this key's content scope (a scoped/siloed key requested content it may not pull). |
| 404 | not_found | Item not found (or not visible to this key). |
| 404 | unknown_benchmark | The requested benchmark id does not exist for this key; the response lists available. |
| 429 | rate_limited | Per-key rate limit exceeded. |
| 500 | server_error | Unexpected server error. |
| 503 | service_unconfigured | The API is temporarily not configured to serve; retry shortly. |