503 errors — the model provider is unavailable
A 503 says the problem is not yours. How to survive it without losing work, and why streamed errors arrive with a 200.
2 min · · Updated · errors · reliability · streaming
A 503 means the service temporarily cannot handle your request. For a model API it usually means the gateway itself is fine but the model provider behind it is not.
A separate case is a 503 with the code model_disabled: the model is disabled in Zukko, and retries will not help. Switch to an enabled model — the current list is GET https://api.zukko.pro/v1/models, and model status is available from the public GET https://api.zukko.pro/api/status.
How it differs from its neighbours
503 — temporary unavailability. The request is valid; retrying makes sense.
500 — an internal error. Also not your fault, but retries help less often: a request that reproducibly breaks processing will break it again.
429 — rate limit exceeded at the model provider; retry with a delay. The Zukko gateway has no rate limit of its own: a 429 from Zukko itself means the key's spending limit (key_daily_limit_reached, reset at 00:00 UTC, or key_monthly_limit_reached), and retrying will not help.
502 — a failure talking to an upstream service.
The practical difference is how long to wait. For a provider 429, follow the Retry-After header. For 503, intervals from a few seconds up to a minute are sensible: if the provider is down, one-second retries only add load.
An important trap: a streamed 503 arrives with a 200 status
This is where hand-written error handling breaks.
On a streaming request the connection opens immediately, headers go out with 200, and only then does an error event arrive inside the stream:
data: {"error":{"message":"The model provider is temporarily unavailable","type":"service_unavailable"}}
data: [DONE]
The status code is 200, so a client that checks only the status treats the request as a success and returns an empty result.
The fix: parse the stream contents, not just the status. An event containing an error field is an error regardless of the response code. And if you keep your own accounting, do not bill such requests — they carry no tokens.
Surviving an outage
Retry with growing delays. Three or four attempts at 5, 15 and 45 seconds cover most short incidents.
Cap the attempts. An unbounded loop during a long outage becomes a hung process.
Persist state before the call. In agent workloads the expensive part of a failure is losing the working context. With state saved, you resume instead of starting over.
Have a fallback model. Outages usually affect one family rather than everything at once. Switching to a comparable model is often cheaper than waiting.
When it is planned maintenance
If the response is instant and consistent, with a clear message about maintenance and a Retry-After header, this is a planned stop rather than an incident. Retrying will not help until it ends — but such requests are not billed either.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- OpenAI API Keys in the Open: Why You Should Not Use ThemWhere free keys on GitHub come from, why using them is dangerous for you and for the owner, and how to protect your own key from the same leak.
- GLM API Errors: Why the Request Fails and How to Fix ItA wrong model id, an overflowing 128K context, the key, the balance, limits: the typical causes of GLM API errors and a quick way to find yours.
- This organization has been disabled in the Claude API: What to DoAn Anthropic key suddenly answers "organization has been disabled". What the error is, what to check, and how to move the SDK and Claude Code to another provider.
- JetBrains AI Assistant not working: causes and workaroundsAI Assistant will not activate, stays silent or is off for some files. We go through the causes in the JetBrains docs and connect your own models via a gateway.