API errors
Zukko reports errors with a regular HTTP status and a JSON body in the OpenAI format. Handle errors by status and error.code, not by text: Zukko's own error messages are in Russian and may change.
1Error format
{
"error": {
"message": "Модель 'gpt-5.4' снята с обслуживания. Используйте 'gpt-5.6-sol' — актуальный список: GET /v1/models. Поддержка — https://t.me/zukkopro?direct, напишите, пожалуйста.",
"type": "invalid_request_error",
"code": "model_retired",
"param": "model"
}
}error.messageis always present;error.codeis set on every Zukko error except401;error.typeanderror.paramappear where they apply.
Every error message ends with a support line: "Поддержка — https://t.me/zukkopro?direct, напишите, пожалуйста." (Support — write to us on Telegram.) A 401 also has a top-level "type": "error" and error.type: "authentication_error". A 403 email_unverified duplicates message and code at the top level.
2Error codes
| Status | Code | When | Retry? |
|---|---|---|---|
| 400 | model_retired | the model was retired; the message names its replacement | no, change the model |
| 400 | invalid_json | the body is not JSON (GLM, web search, images) | no, fix the request |
| 400 | invalid_request | web search without query | no |
| 400 | invalid_request_error | images: no prompt, or no images for an edit model | no |
| 400 | unknown_glm_model | unknown GLM model | no |
| 401 | — (authentication_error) | the key is missing, mistyped, or revoked | no |
| 402 | insufficient_balance | balance below $0.01 (for images — below the price of one image) | after a top-up |
| 403 | email_unverified | email not verified | after verification |
| 403 | model_not_allowed_for_key | the model is not in this key's allowed list | no |
| 404 | model_not_found | the model is not in the catalog; the request does not reach a provider and is not charged | no, fix the name using GET /v1/models |
| 404 | not_found | unknown path | no |
| 405 | method_not_allowed | wrong HTTP method | no |
| 413 | payload_too_large | request body larger than 32 MB | no, make it smaller |
| 422 | idempotency_conflict | the Idempotency-Key was already used with a different body | no, use a new key |
| 429 | key_daily_limit_reached | the key's daily cap is used up | after 00:00 UTC, or raise the cap |
| 429 | key_monthly_limit_reached | the key's monthly cap is used up | next month (UTC), or raise the cap |
| 429 | provider's code | rate limit on the model provider's side | yes, with backoff |
| 502 | upstream_unreachable | the model provider did not respond | yes, with backoff |
| 502 | web_search_failed | web search failed | yes |
| 502 | image_generation_failed | image generation failed | yes |
| 503 | model_disabled | the model is temporarily switched off | no, pick another from GET /v1/models |
| 503 | provider_unavailable | the model provider is temporarily unavailable | yes, with backoff or another model |
| 503 | maintenance | maintenance; comes with Retry-After: 600 | yes, after Retry-After seconds |
| 503 | image_provider_unavailable, image_provider_busy | the image service is down or overloaded | yes, later |
| 504 | image_generation_timeout | image generation took longer than 3 minutes | yes |
Zukko has no request rate limiting of its own. A 429 from Zukko only means a key limit that you set yourself. Any other 429 comes from the model provider.
3Provider errors
Errors returned by the model provider are passed through with the same status and text — for example, context length exceeded or an invalid parameter. Exceptions:
- if the provider answered
402, you get503 provider_unavailable— it is not about your balance; - if the error text explains nothing, Zukko replaces it with its own: for 5xx — "the model provider is temporarily unavailable, retry later", for 4xx — "the model provider rejected the request, check the parameters";
- for 5xx errors Zukko appends the list of models that are working right now.
A 500 with the body {"error": "Internal server error"} means a failure on Zukko's side — retry, and if it persists, contact support.
4Errors inside a stream
With stream: true the 200 status is sent before the model's answer. If an error happens after that, it arrives inside the stream as an event with an error field. Check the stream events, not just the HTTP status. A stream that carried only an error — no text and no usage — is not billed. See Streaming.
5Retrying
- retry provider
429,500,502,503(exceptmodel_disabled), and504with exponential backoff; - on
503 maintenancewait the number of seconds inRetry-After; - do not retry other
4xx— fix the request, key, or balance; - add an
Idempotency-Keyheader: a retry of a request that already completed returns the stored response and is not charged again. Send the retry after the first request has finished — see idempotency.