Docs/Errors & retries

Errors & retries.

Every error response is a JSON envelope with a stable code, a human message, and a request_id you can quote in support. WebSocket errors arrive on the wire as the same envelope.

Envelope

json
{
  "error": {
    "code": "voice_not_found",
    "message": "Voice 'arjun_2' is not in the catalogue. See /docs/voices.",
    "param": "voice",
    "request_id": "req_01HZF8K9XV..."
  }
}

HTTP status codes

StatusCodeWhen
400invalid_requestBody fails schema validation. param identifies the offending field.
401invalid_api_keyMissing, malformed, or revoked key.
403scope_deniedKey lacks permission for the requested voice, language, or IP range.
404voice_not_found / language_unsupportedRequested resource does not exist.
413input_too_longInput exceeds 4 000 characters; chunk the text.
429rate_limitedYou're over the per-second or per-minute cap. Respect Retry-After.
499client_disconnectedYou closed the connection before audio finished. No charge.
500internal_errorTransient. Retry with backoff; if persistent, page us with the request_id.
503capacity_exhaustedRegional pool saturated. Retry on a different region or with backoff.

Retry policy

Retry on 429, 500, 502, 503, and 504. Use exponential backoff with jitter, starting at 250 ms and doubling, capped at 8 s. Stop after six attempts.

python
import time, random
def backoff(attempt):
    return min(0.25 * 2 ** attempt, 8) * (0.5 + random.random())

Idempotency keys

Pass Idempotency-Key: <uuid> to deduplicate retries. Identical retries within 24 hours return the cached audio bytes; differing bodies under the same key are rejected with 409.