Every non-200 answer has the same shape, so you can handle errors generally and look at the code only when you need to.
{
"error": {
"message": "\"messages\" must be an array of 1 to 40 messages.",
"type": "invalid_request_error",
"code": "invalid_request",
"param": null,
"retry_after_seconds": 12
}
}type and param are OpenAI's, so its SDKs read the error: type is invalid_request_error, authentication_error, rate_limit_error or server_error (param is always null). retry_after_seconds comes only with 429 (and a Retry-After header with 429 and 503). message is for the developer reading the logs, not for showing to people as it is.
| Status | code | Meaning | Retry? |
|---|---|---|---|
| 400 | invalid_json | The body isn’t valid JSON | Fix the request first |
| 400 | invalid_request | A field is wrong or unknown; the message says which | Fix the request first |
| 401 | missing_api_key | No Authorization header | Add the header |
| 401 | invalid_api_key | Not a key, or the key doesn’t exist or was revoked | Check your keys on the dashboard |
| 403 | suspended | The key’s account is suspended; the message says until when, and why | No |
| 413 | payload_too_large | The body is over 256 KB | Send less history |
| 415 | unsupported_media_type | Content-Type isn’t application/json | Fix the header |
| 400 | context_length_exceeded | OpenAI-compatible API: the prompt is over your plan’s size (never trimmed) | Shorten it |
| 400 | unsupported_parameter / unsupported_content / tools_not_supported / invalid_tools | OpenAI-compatible API: see its error table | Fix the request first |
| 404 | model_not_found | OpenAI-compatible API: the model isn’t yahoobot-1 or israelbot-1 | Fix the name |
| 429 | rate_limited | Over a limit: the key’s, the account’s, or the address’s | After retry_after_seconds |
| 429 | token_limit | The account used its tokens for today (UTC) | After retry_after_seconds (00:00 UTC) |
| 502 | upstream_unavailable | The model didn’t answer | Yes, with a short backoff |
| 503 | capacity_exceeded | Every key together is over the shared cap (see Rate Limits) | Yes, after a backoff |
| 503 | maintenance | The API is switched off for now | Later: Retry-After says when to look again |
On the OpenAI-compatible API the same thing is a 200 with finish_reason: "content_filter" (see Sizes, safety and errors). A request stopped by the safety checks (someone in crisis, content that breaks the rules) is not an error. It's a normal 200 with flags.crisis or flags.blocked set and a fixed message in reply. See API Reference. Treat a non-200 as the request or the service failing, never as a moderation signal.
A 4xx means something about the request has to change before it will work (the key, the body, or waiting for a limit). A 5xx means the problem is on our side, and the same request may well work on a retry: safe to build a simple retry around. See Examples.