Errors
Every error uses the OpenAI error shape, so SDK exception handling just works.
POSTerror shape
402{ "error": {
"message": "Out of credits. Top up at /dashboard/billing.",
"type": "insufficient_quota",
"code": "insufficient_credits"
} }
Top up, then send it again.retry: after a top-up
Shape
payload.jsonJSON
HTTP/1.1 402{ "error": { "message": "Out of credits. Top up at /dashboard/billing.", "type": "insufficient_quota", "code": "insufficient_credits" }}Branch on the HTTP status and code. The message is for humans and can change. code is null for generic 400s.
Codes
| Status | type | code | When | Do |
|---|---|---|---|---|
| 400 | invalid_request_error | (none) | Body not JSON, missing model, missing or empty messages, or the engine rejected a parameter. | Fix the request. Do not retry. |
| 401 | authentication_error | missing_key | No Authorization or x-api-key header. | Send a key. |
| 401 | authentication_error | invalid_key | Key unknown or revoked. | Create a new key. |
| 402 | insufficient_quota | insufficient_credits | Balance is zero or below. | Top up, then retry. |
| 402 | insufficient_quota | key_spend_limit | This key reached its spend limit. | Raise or reset the limit in the dashboard. |
| 403 | permission_error | model_not_allowed | Key is scoped and this model is not on its list. | Use an allowed model or edit the key. |
| 403 | permission_error | suspended | Account suspended. | Contact support. |
| 404 | invalid_request_error | model_not_found | Unknown model id. | GET /v1/models. |
| 429 | rate_limit_error | rate_limited | Over the per-minute request limit. | Back off and retry. |
| 502 | server_error | (none) | Search or scrape backend failed. | Retry after a moment. Not charged. |
| 503 | server_error | no_capacity | No inference node can take the model right now. | Retry with backoff. |
| 503 | server_error | upstream_unavailable | Every node failed or errored. | Retry with backoff. |
Other 4xx statuses from the model engine are passed through with the engine's message and type invalid_request_error. Typical cause: a parameter value the engine does not accept.
Retry advice
- Retry: 429, 502 and 503, plus network errors and timeouts. Use exponential backoff with jitter: 1s, 2s, 4s, capped around 30s, with a limit of a few attempts.
- Do not retry: 400, 401, 402, 403, 404. The same request will fail the same way.
- For 429 there is no
Retry-Afterheader. Windows are 60 seconds, so waiting a few seconds then slowing down is the right move. - The gateway already fails over between nodes inside a single request, so a 503 means every node tried was unavailable, not that it gave up early.
- Chat completions are not idempotent. A retry after a timeout can bill twice if the first call actually finished. For long generations, stream and keep what you receive.
Related: rate limits, billing, authentication.