Errors
HTTP status codes, the error envelope, and recommended retry behavior for every AnyRouter API error.
AnyRouter uses standard HTTP status codes and wraps every error in a consistent envelope. Read the status code to decide whether to retry, and the code field to branch on the specific failure.
{
"error": {
"message": "Human-readable message",
"code": "machine_code",
"metadata": {
"type": "error_category"
}
}
}
Status codes
| Status | Type | Retry? | Description |
|---|---|---|---|
400 | invalid_request_error | No | Malformed request — fix and re-send. |
401 | authentication_error | No | Missing or invalid API key. |
403 | permission_error | No | Key lacks the required scope. |
404 | not_found_error | No | Model or resource does not exist. |
409 | conflict_error | No | Conflicting state (e.g. duplicate key name). |
413 | request_too_large | No | Input exceeds the model's context window. |
422 | invalid_request_error | No | Request shape is valid but semantically wrong. |
429 | rate_limit_error | Yes, with backoff | Rate limit hit — honor Retry-After. |
499 | client_closed_request | No | Client disconnected before response. |
500 | internal_server_error | Yes | AnyRouter internal issue. |
502 | upstream_error | Yes | Upstream provider returned a 5xx. |
503 | service_unavailable | Yes | No healthy upstream available. |
504 | upstream_timeout | Yes | Upstream provider timed out. |
Never retry 4xx errors other than 429. A 401 will keep returning 401 until you fix the auth header — retrying just burns rate-limit budget.
Retry retryable errors
For retryable errors (429, 5xx), use exponential backoff with jitter, and honor the Retry-After header when present:
async function withRetry<T>(fn: () => Promise<T>, attempts = 5): Promise<T> {
for (let i = 0; i < attempts; i++) {
try {
return await fn()
} catch (err) {
if (i === attempts - 1) throw err
if (!isRetryable(err)) throw err
const base = Math.min(1000 * 2 ** i, 30_000)
const jitter = Math.random() * 250
await new Promise((r) => setTimeout(r, base + jitter))
}
}
throw new Error("unreachable")
}
Common error codes
The code field names the exact failure. Expand each for what it means and how to fix it.
authentication_required
The route requires a signed-in session or bearer key. Add an Authorization: Bearer header with a valid key.
missing_api_key
The Authorization header was required but missing. Send Authorization: Bearer sk-ar-your-key.
invalid_api_key
The supplied bearer key was invalid, revoked, or inactive. Create a fresh key in the dashboard.
insufficient_balance
The account has run out of credits for a paid-model request. Add credits or subscribe. Free-tier models do not use this code. They use the daily cap (429 free_tier_daily_limit_exceeded) instead.
When you see 403
A 403 is a permission gate — retry the same request will not help; you must change something on the account, the key, or the model. The three that come up most:
model_not_allowed
Your key's allow-list excludes the requested model (per-key routing preferences), or the model is disabled for your account. Edit the key's allow-list in the dashboard or pick another provider/model from the catalog.
permission_error
Your key lacks the scope for this endpoint (e.g. a management key on /chat/completions, or an inference key on /keys). Mint a key with the right group in the dashboard.
model_not_found
The requested model slug doesn't exist in the catalog. Use a full provider/model id from the catalog.
context_length_exceeded
Input is longer than the model supports. Shorten the prompt or choose a model with a larger context window.
rate_limit_exceeded
Per-key or per-IP rate limit exceeded (rate_limit_exceeded, ip_rate_limit_exceeded). Back off and honor the Retry-After header.
free_tier_daily_limit_exceeded
You used today's free-model allowance (anyrouter/free or a :free variant). Free is 10 requests/day. Paid plans are 1000/day. The cap resets at 00
upstream_unavailable
All upstream providers for this model are down. Retry, or route to a different model.
content_policy
An upstream refused the request due to a content-policy filter. Adjust the prompt.
Debugging
Every inference response includes an X-Request-ID header (also sent under the canonical name X-AnyRouter-Trace-Id — the two carry the same value). Include it when reporting bugs — it lets us trace your exact request lifecycle across auth, rate-limit, routing, and upstream dispatch.
curl -i https://anyrouter.dev/api/v1/chat/completions \
-H "Authorization: Bearer sk-ar-your-key" \
-H "Content-Type: application/json" \
-d '{"model": "openai/gpt-5.4-mini", "messages": [{"role": "user", "content": "hi"}]}'
# HTTP/2 200
# x-request-id: req_01HQ...
Send a request-body trace object on Chat Completions or Responses and AnyRouter carries your trace_id through the internal step logs and AI Gateway correlation metadata — useful for lining up your app traces with AnyRouter routing behavior.