Errors
The API uses conventional HTTP status codes and a single, predictable JSON error shape, so you can handle failures the same way everywhere.
Error shape
Failed requests return a JSON body with an error string and, where helpful, a more detailed message:
{
"error": "Rate limit exceeded. Maximum 20 generations per hour.",
"message": "Try again after the window resets."
}The HTTP status code is the source of truth for the failure category; the error string is safe to surface to end users.
Status codes
| Status | Meaning |
|---|---|
200 / 201 | Success. 201 is returned when a resource is created. |
400 | Bad request: validation failed or required fields are missing. |
401 | Unauthorized: missing or invalid credentials. |
403 | Forbidden: authenticated, but no access to this organization or resource. |
404 | Not found: the resource (or its parent org/project) does not exist. |
429 | Too many requests: a rate limit was hit. See Rate limits. |
500 | Internal error: something failed on our side. Safe to retry with backoff. |
502 / 503 | Upstream or agent dependency unavailable. Retry with backoff. |
Handling errors
Branch on the status code. The error string is human-readable and may change, so keep it for display. Treat 400/401/403/404 as terminal (fix the request), and 429/500/502/503 as retryable with exponential backoff. Validation errors (400) describe the offending field in error.
const res = await fetch(url, { headers: { Authorization: `Bearer ${jwt}` } });
if (!res.ok) {
const { error } = await res.json().catch(() => ({ error: res.statusText }));
if (res.status === 429 || res.status >= 500) {
// retry with exponential backoff
}
throw new Error(`[${res.status}] ${error}`);
}