Error handling
The error format depends on the address you sent the request to. Every error carries a request ID.
Error format
On /v1/chat/completions, /v1/responses and all other addresses the error arrives 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…"
}
}On /v1/messages the error arrives in the Anthropic format, which the Anthropic SDK and Claude Code understand:
{
"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. Send it to support: it lets us find the request.
Error codes
The values in the code column come in the OpenAI format. In the Anthropic format the same cause shows up as the HTTP status and the error type.
| HTTP | code | What to do |
|---|---|---|
400 | model_required bad_request invalid_request | Check the request body: JSON and required fields. |
400 | model_not_found | Check the model name in GET /v1/models. |
400 | context_length_exceeded | Shorten the request or use a model with a larger context. |
400 | image_input_not_supported | The model does not accept 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 can arrive as unauthorized, 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 | The model is not in this key's channel: 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 | Too many requests, retry in a few seconds. |
429 | org_queue_full | Too many concurrent requests 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 almost empty, 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 | Temporarily unavailable, retry in a few seconds. |
Channel selection errors (channel_not_allowed, fallback_not_allowed, channel_unavailable, invalid_state) are covered on the Premium and Cheap channels page.
Retry-After
Responses with 429 and 503 carry a Retry-After header: the wait time in seconds.
Errors in a stream
If an error happens after the stream has started, the response code is already 200, so the error arrives as an event:
- OpenAI format:
data: {"error": {…}}, thendata: [DONE]; - Anthropic format:
event: errorwith the error body; - Responses API: the
response.failedevent.
If the model cut the answer off midway, you get an error with the code upstream_stream_interrupted or stream_incomplete. The answer is unfinished, so retry the request.
Billing on errors
If the error happens before the answer starts, nothing is charged. If the answer is cut off midway, you are charged only for the tokens the model already produced.
Retry strategy
We recommend this order:
- 429 and 503: retry with a delay that starts at 1 second and doubles (1, 2, 4, 8 seconds, at most 60). If Retry-After is present, wait as long as it says.
- 500 and 502: retry up to 3 times with a one-second pause. If the error persists, write to support and attach the request ID.
- 400, 401, 402, 403, 404, 413: do not retry. 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.claudexia.tech/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")