Errors and retries
Things break. Good API clients make that boring: read the status, keep the
request_id, and retry only when the failure is actually temporary.
Core error envelope
Authentication, model, rate-limit, and service failures use this shape:
{
"error": {
"message": "Invalid API key.",
"type": "authentication_error",
"param": null,
"code": "invalid_key"
},
"request_id": "<request-id>"
}
Use error.code for program logic, show or log error.message, and retain the
request_id for support.
Some TTS validation and generation errors use a detail string or array
instead:
{
"detail": "Unsupported response_format. Supported formats: wav, mp3, flac, aac, opus, pcm"
}
Clients should handle both documented JSON shapes for TTS 400, 422, and
500 responses.
Status codes
| Status | Meaning | Client action |
|---|---|---|
400 | Invalid body, field, audio, or realtime event | Correct the request; do not retry unchanged |
401 | Missing, malformed, invalid, expired, revoked, or incorrectly scoped key | Replace or update the key |
402 | Insufficient credit or suspended billing state | Resolve billing or account state |
404 | Wrong path or unsupported model | Check the URL and model ID |
413 | Request or file exceeds an endpoint limit | Reduce or split the input |
422 | TTS parameter validation failed | Correct the named field |
429 | Account or migrated-key concurrency limit reached | Respect Retry-After; retry with backoff and jitter |
502 | Inference service failed | Retry with capped exponential backoff |
503 | Authentication, billing, or inference service unavailable | Retry with capped exponential backoff |
Other 5xx | Unexpected server failure | Retry a limited number of times |
Common error codes
| Code | Meaning |
|---|---|
missing_api_key | The bearer credential is absent |
malformed_key | The credential does not have a valid KittenML key shape |
invalid_key | The credential is unknown, revoked, or otherwise invalid |
scope_not_allowed | The key lacks the permission required by the endpoint |
insufficient_credit or out_of_credits | Paid ASR cannot begin with the available balance |
invalid_file | The upload is empty, oversized, or cannot be decoded |
invalid_response_format | The requested transcription representation is unsupported |
rate_limit_exceeded | The applicable concurrency pool is full |
billing_service_unavailable | Authentication or paid-usage state cannot be checked temporarily |
tts_service_unavailable | Speech generation is temporarily unavailable |
Other endpoint-specific codes can be returned. Use the HTTP status for the
broad error class and error.code for specific handling.
WebSocket errors
Realtime authentication and admission failures arrive as a structured error
event after the WebSocket upgrade, followed by a close frame:
{
"type": "error",
"error": {
"type": "authentication_error",
"code": "invalid_key",
"message": "Invalid API key."
},
"request_id": "<request-id>"
}
The current public endpoint exposes close code 1000 for these failures.
Always use error.code to classify the failure; the close code is only a
transport-level signal that the session ended.
Retry safely
For retryable HTTP failures, use capped exponential backoff with random jitter.
Limit the number of attempts, set a request timeout, and honor Retry-After
when the response includes it. Do not retry an unchanged request for a status
listed above as a client or account error.
A fresh client retry is a new inference request. If both the original and the retry succeed, both may be processed and recorded separately.