API errors
The error format is determined by the address the request went to. Every error carries a request ID.
Error format
For /v1/chat/completions, /v1/responses and all other addresses, the error comes in the OpenAI format:
{
"error": {
"message": "Invalid or missing API key. Request ID: req_0be8…",
"type": "authentication_error",
"code": "authentication_error",
"request_id": "req_0be8…"
}
}For /v1/messages, the error comes in the Anthropic format, which the Anthropic SDK and Claude Code can parse:
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid or missing API key. Request ID: req_0be8…"
},
"request_id": "req_0be8…"
}The request ID is in the request_id field, at the end of the message text and in the X-Request-Id header. Attach it when you contact support, and we will find the request by it.
Error codes
The values in the code column belong to the OpenAI format. In the Anthropic format the same cause is expressed by the HTTP status and the error type.
| HTTP | code | What to do |
|---|---|---|
400 | model_required bad_request invalid_request | Check the JSON and the required fields of the request body. |
400 | model_not_found | Check the model name in GET /v1/models. |
400 | context_length_exceeded | Reduce the request or choose a model with a wider context. |
400 | image_input_not_supported | The model does not take images: use a model with vision. |
400 | content_policy_violation | The request violates the usage policy. |
401 | authentication_error | The key is wrong, revoked or missing. On image and video addresses the code field may be unauthorized, while the type stays authentication_error. |
402 | insufficient_quota | Top up your balance. |
402 | account_frozen | The account is suspended, write to support. |
403 | model_not_in_channel | This key's channel does not have the model: change the key's channel or the model. |
404 | not_found | Unknown API address. |
413 | request_too_large | The request body is larger than 32 MB. |
429 | rate_limited family_rate_limited | Requests are coming too fast, retry in a few seconds. |
429 | org_queue_full | Too many requests are running at once on this key: wait for the current ones to finish. |
429 | queue_full queue_timeout | The service is busy, retry in a few seconds. |
429 | low_balance | The balance is nearly used up, so requests run one at a time. Top up your balance. |
500, 502 | server_error empty_response | Processing failed, retry the request. |
503 | model_overloaded | The model is overloaded, retry later. |
503 | maintenance auth_unavailable service_unavailable | The service is temporarily unavailable, retry in a few seconds. |
Channel selection errors (channel_not_allowed, fallback_not_allowed, channel_unavailable, invalid_state) are described on the Premium and Cheap channels page.
Retry-After
Responses with 429 and 503 include a Retry-After header with the wait time in seconds.
Errors in a stream
If a failure happens after the stream has started, the response code stays 200 and the error arrives as a separate event:
- in the OpenAI format:
data: {"error": {…}}, thendata: [DONE]; - in the Anthropic format:
event: errorwith the error body; - Responses API: the
response.failedevent.
If the model's answer breaks off midway, you get an error with the code upstream_stream_interrupted or stream_incomplete. The answer is incomplete, so repeat the request.
Billing on errors
When the error comes before the answer starts, nothing is charged. When the answer breaks off midway, you pay only for the tokens the model managed to produce.
Retry strategy
We recommend this order:
- 429 and 503: retry after a pause that starts at 1 second and doubles each time (1, 2, 4, 8 seconds, capped at 60). If Retry-After arrives, wait exactly as long as it says.
- 500 and 502: retry up to 3 times with a one-second pause. If the error does not go away, contact support and attach the request ID.
- 400, 401, 402, 403, 404, 413: retrying will not help. Fix the request, the key, the balance or the channel.
import time
import requests
def call_api(payload, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
"https://api.codexkey.online/v1/messages",
headers={
"x-api-key": "sk_cdx_YOUR_KEY",
"anthropic-version": "2023-06-01",
"Content-Type": "application/json",
},
json=payload,
)
if response.status_code == 200:
return response.json()
if response.status_code in (429, 503):
wait = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
continue
if response.status_code in (500, 502):
time.sleep(1)
continue
response.raise_for_status()
raise Exception("Max retries exceeded")