Errors
Every response uses a consistent envelope. On failure you get a machine-readable code and an HTTP status — never a stack trace or an internal service name.
Response envelope
Success and failure are both wrapped, distinguished by the success flag:
json
// success
{ "success": true, "data": { "the": "payload" } }
// error
{ "success": false,
"error": { "code": "NOT_FOUND", "message": "Content not found.", "statusCode": 404 } }Status codes
| Param | Type | Description |
|---|---|---|
| 401 UNAUTHORIZED | error | Missing, invalid, expired, or revoked API key. Check your token. |
| 403 FORBIDDEN | error | The project in the path does not match the key’s project, or the scope is wrong. |
| 404 NOT_FOUND | error | Unknown content type apiId, unknown slug, or no published entry matches. |
| 422 VALIDATION_ERROR | error | Malformed query parameters (bad sort key, limit over 100, unknown select field, etc.). |
| 429 RATE_LIMITED | error | Monthly Delivery request quota exceeded, when usage enforcement is enabled. See Rate Limits. |
| 500 INTERNAL_ERROR | error | Server-side failure. Safe to retry; transient. |
Handling errors
Check the HTTP status (or the success flag) before reading the payload:
ts
const res = await fetch(url, { headers: { Authorization: 'Bearer ' + token } });
const body = await res.json();
if (!res.ok || body.success === false) {
const err = body.error ?? { code: 'REQUEST_FAILED', message: 'Request failed.' };
throw new Error('[' + err.code + '] ' + err.message);
}
const data = body.data;// SAFE BY DESIGNError messages are generic and never leak internals — database errors, service names, and stack traces are all stripped before the response leaves the gateway.
Rate Limits & Usage