All errors follow a consistent JSON format with a machine-readable code and human-readable message.
{
"error": {
"code": "validation_error",
"message": "keyword: Required",
"details": { ... },
"request_id": "req_abc123"
}
}
| Field | Description |
|---|
code | Machine-readable error code (see table below) |
message | Human-readable explanation |
details | Additional context (optional) |
request_id | Unique request identifier for support (optional) |
Always include the request_id when contacting support about a failed request. It allows the team to trace the exact request through the system.
Error Handling Decision Tree
Use this diagram to determine the correct recovery strategy for each error class:
Do not retry 4xx errors (except 429). These indicate a problem with the request itself — retrying the same request will always fail. Fix the issue first.
HTTP status codes
| Status | Meaning |
|---|
200 | Request succeeded |
201 | Resource created |
202 | Async operation accepted and started |
204 | Deleted (no response body) |
400 | Invalid request parameters |
401 | Authentication failed |
403 | Authenticated but lacking permissions |
404 | Resource not found or not accessible |
409 | Idempotency conflict |
429 | Rate limit exceeded |
500 | Server error |
Error codes
| Code | Status | Description |
|---|
validation_error | 400 | Request body or parameters failed validation |
unauthorized | 401 | Missing or invalid API key |
key_expired | 401 | API key has expired |
key_revoked | 401 | API key has been revoked |
forbidden | 403 | Key lacks the required scope |
not_found | 404 | Resource does not exist or is not accessible to this key |
idempotency_conflict | 409 | Same idempotency key used with different parameters |
rate_limit_exceeded | 429 | Too many requests |
internal_error | 500 | Unexpected server error |
For 429 errors, see the Rate Limiting guide for retry logic examples with exponential backoff.
Handling errors
const response = await fetch(url, { headers });
if (!response.ok) {
const { error } = await response.json();
switch (error.code) {
case "validation_error":
console.error("Bad request:", error.message);
break;
case "rate_limit_exceeded": {
const retryAfter = response.headers.get("Retry-After");
await new Promise((r) => setTimeout(r, Number(retryAfter) * 1000));
break;
}
case "unauthorized":
case "key_expired":
case "key_revoked":
console.error("Auth issue:", error.message);
break;
default:
console.error("API error:", error.code, error.message);
}
}