Errors
Every non-2xx response carries the same JSON envelope:
{ "error": { "code": "scope_required", "message": "Missing required scope", "details": { "scope": "photos:write" } }}| Field | Type | Purpose |
|---|---|---|
code | string | Machine-readable identifier. Branch on this in your code. |
message | string | Human-readable explanation. Don’t branch on this — wording may change. |
details | object | null | Optional structured context for the error (missing scope, validation issues, …). |
Error codes
Section titled “Error codes”| HTTP | code | When |
|---|---|---|
| 400 | validation_failed | Request body or query failed schema validation. details lists per-field issues. |
| 400 | invalid_cursor | Pagination cursor expired or malformed. Restart pagination without cursor. |
| 401 | unauthorized | Missing, malformed, or unknown API key. |
| 403 | scope_required | Key valid but lacks the scope the endpoint requires. details.scope names the missing one. |
| 403 | ip_not_whitelisted | Source IP not in the key’s whitelist. |
| 404 | not_found | Resource does not exist, or the key cannot see it (multi-tenant isolation). |
| 409 | conflict | Operation conflicts with current state (e.g. tag with that name already exists). |
| 413 | payload_too_large | Upload or request body exceeds the per-endpoint limit. |
| 415 | unsupported_media | Content-Type not accepted by this endpoint. |
| 429 | rate_limited | Request budget exhausted. See Rate limits. |
| 403 | tariff_not_supported | Customer’s subscription tier does not include this feature. Upgrade the plan or remove the call. Not retryable. |
| 500 | internal_error | Unexpected server failure. Safe to retry with backoff; if persistent, contact support with the requestId. |
Request ID
Section titled “Request ID”5xx responses include an X-Request-Id header. Include it when reporting issues — it lets us trace the failure end-to-end in seconds rather than minutes.
HTTP/1.1 500 Internal Server ErrorX-Request-Id: req_01HXYZ123ABCContent-Type: application/json
{ "error": { "code": "internal_error", "message": "Something went wrong" } }Validation errors
Section titled “Validation errors”validation_failed returns per-field detail in details.issues:
{ "error": { "code": "validation_failed", "message": "Request body failed validation", "details": { "issues": [ { "path": "name", "message": "Required" }, { "path": "scopes", "message": "Must contain at least one scope" } ] } }}path is the dotted JSON path inside the request body or ?query= for query parameters.
Best practices
Section titled “Best practices”- Branch on
code, notmessage. Messages are tuned for humans and may change. Codes are part of the API contract. - Don’t retry 4xx. A bad request stays bad. Only
429and transient5xxmake sense to retry. - Log the
requestIdfrom 5xx responses. It’s the fastest path to a fix when something goes wrong.