Skip to main content
The API returns errors as JSON. The HTTP TTS endpoints (/tts/bytes, /tts/sse) use a four-field envelope; /voices/clone uses a shorter two-field shape.

Error Format

/voices/clone uses the shorter { "error": "...", "message": "..." } shape (no error_code/detail). WebSocket errors arrive as a Cartesia-shaped error frame with error_code, status_code, context_id, and done: true.

Error Code Reference

Errors

unauthorized (401)

The API key is missing, malformed, invalid, or revoked.
Fix: Send a valid, non-revoked key via Authorization: Bearer sk_996_... (or ?api_key= for WebSockets).

missing_transcript (400)

The transcript is empty, whitespace-only, or (on WebSocket) omitted. On the WebSocket a message that omits transcript returns this error frame instead of a silent done — but an empty string with continue: false is the valid end-of-turn flush marker, not an error.

missing_voice / missing_voice_id / unknown_voice_mode (400)

The voice field is required and must be the nested object { "mode": "id", "id": "<voice-id>" }. There is no default voice — a request that doesn’t resolve to a voice is rejected (it is no longer silently substituted). A top-level voice_id is not supported.

voice_not_found (404)

The voice.id doesn’t exist or belongs to another organization.
Fix: List available voices with GET /voices and pass an id your org owns.

input_too_long (400)

A single request can synthesize at most ~46.5 seconds of audio — the generator’s hard ceiling. The limit is on predicted spoken duration, not character count: the estimate is digit-aware and per-language, and a digit is spoken as a whole word (4 → “four”), so number-heavy text hits the limit with far less text. As a rough en-US guide, that’s ~900 characters of normal prose but only ~130 digits (phone numbers, amounts, codes, dates). The exact threshold shifts slightly by voice language. The check runs before synthesis, so nothing is billed on a rejection. On /tts/bytes and /tts/sse this is an HTTP 400 (on SSE it’s returned before the event stream opens, as a normal HTTP body — not an in-stream event):
On the WebSocket it’s a per-context error frame; the socket stays open and other contexts/turns are unaffected:
Fix: Keep each request under ~46.5s of speech (~900 characters of prose, fewer for number-heavy text) and split longer content into multiple requests. Over WebSocket, stream it across turns with continue: true, keeping each turn under the limit.

payload_too_large (413)

On /tts/bytes and /tts/sse, the request body exceeds the 64 KB (65536-byte) cap. Normal requests are nowhere near this — it’s a safety limit. Uses the shorter { error, message } shape.

unknown_model (400)

The model_id is not a supported model. Send gepard-1.0.

invalid_language (400)

On POST /voices/clone, the language form field is not a well-formed BCP-47 tag. Send the full dialect (e.g. en-US, en-GB, es-MX, pt-BR, nl-NL); malformed tags (en_US, en-USA, english_, 123) are rejected. The value is canonicalized on save (en-usen-US). This endpoint uses the shorter { error, message } shape.

Invalid or malformed body (400)

The request body isn’t valid JSON, or a required field (model_id, transcript, voice, output_format) is missing.

Invalid output_format (400)

The output_format uses an unsupported container, encoding, or sample_rate. Validation is strict — there’s no silent fallback. The message names the offending field. Common causes:
  • Removed formats: container: "mp3", encoding: "pcm_f32le"
  • Unsupported sample_rate (only 8000, 16000, 22050 are allowed)
  • container: "wav" on a streaming endpoint (/tts/sse, /tts/websocket are raw-only)
Fix: Use a supported combination — see Create Speech → Output Format.

payment_required (402)

The organization’s credit balance can’t cover the request.
Fix: Top up on the Billing page.

rate_limited / concurrent_limit (429)

You’ve exceeded your org’s per-minute request rate or concurrency cap (shared across all your API keys). See Rate Limits. Fix: Honor the Retry-After header and retry. For concurrent_limit, retry the rejected turn on a fresh context_id — only that context was rejected, not the whole connection.

billing_unavailable (503)

Credits couldn’t be verified, so the request fails closed (no audio is generated and you aren’t charged). Fix: Retry shortly.

upstream_unavailable (502)

The synthesis backend errored. Any pre-charge is refunded automatically. Fix: Retry the request. If it persists, contact support.

Handling Errors in Code