Errors & retries
All errors return a JSON body in the same shape:
{
"error": {
"message": "This model is temporarily unavailable: its backend is down or being updated. Retry after the Retry-After delay.",
"type": "service_unavailable",
"code": "model_unavailable"
},
"docs": "https://appelon.ai/docs/errors"
}
Branch on error.code in your client, not on the message text. Messages may change; codes are stable.
Error codes
| HTTP | error.code |
Meaning | Retry? |
|---|---|---|---|
400 |
invalid_request |
Malformed request or unknown model name. Check GET /v1/models. |
No |
401 |
auth_error |
Missing, invalid, or revoked API key. | No |
402 |
quota_error |
Monthly quota or trial credits exhausted. | No |
403 |
license_error |
License canceled, past due, or archived. | No |
403 |
mfa_required |
Your organization requires two-factor login and the person who created this key has not set it up. | No, until they do |
403 |
auth_error |
The person who created this key is no longer a member of the organization. | No |
503 |
model_unavailable |
The model’s backend is down, restarting, or loading a model update. | Yes, after Retry-After |
503 |
capacity_error |
All GPU capacity is in use and the queue wait timed out. | Yes, after Retry-After |
502 |
upstream_error |
The backend accepted the request but failed while processing it. | Yes, with backoff |
500 |
server_error |
Unexpected error in the gateway. | Yes, with backoff |
Model updates and maintenance
When we roll out a model update, its backend is briefly unavailable while the new
weights load (typically a few minutes). During that window requests fail fast with
503 and error.code: "model_unavailable", plus a Retry-After header (in
seconds). Your request is not lost or half-processed: it never reached the model,
so it is always safe to retry.
A simple retry loop:
import time
import requests
def chat(payload, headers, max_attempts=5):
for attempt in range(max_attempts):
resp = requests.post(
"https://router.appelon.ai/v1/chat/completions",
json=payload, headers=headers,
)
if resp.status_code != 503:
return resp
wait = int(resp.headers.get("Retry-After", 30))
time.sleep(wait)
return resp
Most OpenAI-compatible SDKs already retry 503 responses automatically; if you
use the official OpenAI client with max_retries set, no extra handling is
needed.
Busy periods
capacity_error means all GPU slots were taken for longer than the queue wait
window. Retrying after the Retry-After delay usually succeeds. Streaming chat
requests are queued instead: while you wait for a slot you receive valid
chat.completion.chunk events with an extra queue field describing your
queue status, followed by the normal response stream.
Asynchronous transcription jobs
The transcription jobs endpoints use the same error format, with two codes of their own: 409 when you try to cancel a job that is already running, and 429 (rate_limit_error) when your account already has 10 open jobs. A job that fails after it was accepted does not return an HTTP error: the job’s status becomes failed and its error field holds one of the codes above. See When a job fails.