429, 5xx, and Timeouts: Engineering Practices for LLM Gateway Failover
Upstream rate limiting and failures are the norm, not the exception, for LLM applications. This article breaks down the correct handling for 429/5xx/timeout/401, when you can automatically switch channels, when you must never retry, and how to avoid double billing.
Classify first, then decide how to handle it
'Retry every error three times' is the most common wrong approach. Different error codes have completely different semantics, and the handling strategy must differ too. The table below is the strategy our gateway actually uses, and it can serve as a reference for your own implementation.
- Connection failure / DNS / TLS: switch to the backup channel immediately.
- Timeout (output has not started yet): switch to the backup channel, at most 1–2 times.
- HTTP 429: cool down briefly, then switch channels, and lower that channel's weight at the same time.
- HTTP 5xx: count it in the failure window; consecutive failures trigger the circuit breaker.
- 401 / 403: credential or permission problem; disable that credential and alert, do not retry.
- Insufficient balance: pause that channel and alert.
- 400 parameter error: after normalization, return it directly to the customer; do not retry, do not bill.
- SSE interrupted after partial output: return
upstream_stream_error; by default do not retry.
Why an interrupted SSE stream cannot simply be retried
Once a streaming response has started outputting tokens to the client, it means the upstream has already produced output (and will bill for it). If the gateway quietly switches to another channel and starts over, the user sees duplicated content, and you pay for both outputs.
The correct approach is to treat this half-finished stream as a partial success: return an error code telling the client 'upstream interrupted', and let the business side decide whether to show the existing content plus a retry prompt, or start over entirely.
Circuit breaker: the layer more important than retries
Retries handle a single request; the circuit breaker judges the overall state over a period of time. When a channel's consecutive failures reach a threshold, mark it 'open', stop sending traffic to it for a while, then let a small amount of probe traffic through (half-open), and after several successful probes fully restore it (closed).
Without a circuit breaker, retries will crush an already-dead channel even harder and cause the customer's average latency to spike.
Retry idempotency and billing
- Non-streaming requests: after retries, bill only the usage of the final successful attempt.
- Streaming requests: the attempt that already output tokens really incurs upstream cost and must be recorded; you cannot wipe the ledger clean just because of a retry.
- Assign a
request_idto each user request; all retries share it, so you can fully replay later 'why this channel was chosen'. - Refunds/adjustments must go through ledger entries; you cannot directly change the balance.
What Admesh already does
The strategy above is the default behavior in the Admesh gateway: automatic switching among multiple upstream channels for the same public_model, every routing decision is persisted, and customers can see request_id, the model actually used, and channel health in their usage records; error codes are returned in a unified format (error.type / error.code / request_id).
It is worth noting: routing only happens within the channel pool of the same public_model; it will not switch you to another vendor's model just because you requested anthropic/xxx—cross-model substitution directly changes output quality, and that is a decision we cannot make for you.
Admesh live rates (excerpt)
Prices come from the live Admesh configuration, in USD per million tokens. See the model catalog for the full list.
| Model Name and ID | Context Window | Enter price / 1M | Output Price / 1M | Cache Hits / 1M |
|---|---|---|---|---|
Claude Fable 5 anthropic/claude-fable-5 | 1M | $3.25 / 1M | $16.25 / 1M | $0.325 / 1M |
Claude Fable 5.1 anthropic/claude-fable-5-1 | 1M | $3.25 / 1M | $16.25 / 1M | $0.08125 / 1M |
Claude Haiku 4.5 anthropic/claude-haiku-4-5-20251001 | 200K | $0.325 / 1M | $1.625 / 1M | $0.0325 / 1M |
Claude Haiku 5.5 anthropic/claude-haiku-5-5 | 1M | $0.0325 / 1M | $0.1625 / 1M | $0.00325 / 1M |
Claude Opus 4.5 anthropic/claude-opus-4-5-20251101 | 200K | $1.625 / 1M | $8.125 / 1M | $0.1625 / 1M |
Claude Opus 4.6 anthropic/claude-opus-4-6 | 1M | $1.625 / 1M | $8.125 / 1M | $0.1625 / 1M |
Claude Opus 4.7 anthropic/claude-opus-4-7 | 1M | $1.625 / 1M | $8.125 / 1M | $0.1625 / 1M |
Claude Opus 4.8 anthropic/claude-opus-4-8 | 1M | $1.625 / 1M | $8.125 / 1M | $0.1625 / 1M |
The above are the standard unit prices for public channels; billing is based on actual token usage. View the complete model catalog
FAQ
On a 429, should I sleep or switch models?
What is a suitable timeout value?
Will retries cause duplicate charges?
References
Call these models through one gateway
Admesh provides a unified OpenAI-compatible API: one Base URL and one key for Claude, GPT, DeepSeek and more. New accounts get trial credit that never expires.
