Other clients

Cartesia API drop-in

Cartesia's own text-to-speech API, bytes, SSE and WebSocket, through the cache: keep Cartesia's SDK, change the base URL and key.

View as Markdown

Cartesia's Python SDK#

Cartesia’s text-to-speech API, mirrored under https://api.ttscacherouter.dev/cartesia. Point Cartesia’s SDK at the router and use your router API key as the API key.

import osfrom cartesia import Cartesiaclient = Cartesia(    api_key=os.environ["TTS_CACHE_ROUTER_API_KEY"],    base_url="https://api.ttscacherouter.dev/cartesia",)response = client.tts.generate(    model_id="sonic-3",    transcript="Your order has shipped. It arrives on Friday.",    voice={"mode": "id", "id": "f786b574-daa5-4673-aa0c-cbe3e8534c02"},    language="en",    output_format={"container": "wav", "encoding": "pcm_s16le", "sample_rate": 24000},)response.write_to_file("speech.wav")

The SDK takes the router in base_url (bytes, SSE and voices) and websocket_base_url. generate, generate_sse and websocket_connect work through the router. Requests, responses, messages and errors are Cartesia’s own, sent to Cartesia with the Cartesia key stored under Provider keys. For a LiveKit agent, see Cartesia with LiveKit.

WebSocket#

wss://api.ttscacherouter.dev/cartesia/tts/websocket speaks Cartesia’s WebSocket protocol: contexts by context_id, answered with chunk, timestamps, flush_done and done messages.

Send your router API key as Cartesia’s clients send theirs, and the API version in Cartesia-Version or the cartesia_version query parameter, as at Cartesia. Contexts run side by side on one connection, each with its own settings, and a connection with no message for 300 seconds is closed, as at Cartesia.

The router splits each context’s text into sentences and serves each sentence from the cache or synthesizes it at Cartesia, so the messages are Cartesia’s with these differences:

  • Each sentence is a take of its own: it doesn’t continue from the audio of the sentence before, as text in one Cartesia context does, and the pauses between sentences are shorter.
  • With max_buffer_delay_ms: 0 (as LiveKit’s plugin sends it), each message’s text is synthesized as it comes. With buffering, a sentence is synthesized once it ends, and a trailing fragment after max_buffer_delay_ms without more text.
  • Word timestamps run on across a context, as Cartesia’s do.
  • add_phoneme_timestamps is refused with an error for that context.
  • cancel drops the context’s sentences not yet sent to Cartesia; one being synthesized finishes and is cached. You get done at once.
  • Cartesia checks a context’s settings when its first sentence is synthesized, so an error for a bad setting comes then. A context served entirely from the cache isn’t checked.

Bytes, SSE and voices#

  • POST /cartesia/tts/bytes: the audio in the body’s output_format, with Cartesia’s own Content-Type.
  • POST /cartesia/tts/sse: Cartesia’s server-sent events, chunk and timestamps then done.
  • GET /cartesia/voices and GET /cartesia/voices/{id}: Cartesia’s voice library and your own voices, never cached. GET /v1/voices lists them in the router’s own shape.

Any other path under /cartesia answers Cartesia’s 404 page not found.

curl "https://api.ttscacherouter.dev/cartesia/tts/bytes" \  -H "X-API-Key: $TTS_CACHE_ROUTER_API_KEY" \  -H "Cartesia-Version: 2026-08-14" \  -H "Content-Type: application/json" \  -d '{"model_id": "sonic-3", "transcript": "Your order has shipped.",       "voice": {"mode": "id", "id": "f786b574-daa5-4673-aa0c-cbe3e8534c02"}, "language": "en",       "output_format": {"container": "mp3", "sample_rate": 44100, "bit_rate": 128000}}' \  --output speech.mp3

What is cached#

  • Bytes and SSE: each request as a whole. The same body again replays the same audio (on SSE, whatever its context_id).
  • WebSocket: each sentence, so a sentence you have sent before with the same settings comes from the cache whatever came around it. Sentences share the cache with the router’s own /v1/stream when the settings are the same.
  • A request with pronunciation_dict_id is cached by the dictionary’s id: if you edit the dictionary, entries cached before keep the old pronunciation until your cache retention expires them.

Errors#

Errors come in the shape of your API version: Title: Message text before 2026-03-01, JSON with error_code, title, message and request_id from it on. A missing or wrong router key gets Cartesia’s own refusal, HTTP 401, at the WebSocket handshake too. An org with no Cartesia key stored gets the same, with a message that links to the dashboard. Errors from Cartesia, such as an unknown voice, reach you as Cartesia sent them; on the WebSocket, as an error message for that context, and the connection stays open.

HTTP/1.1 401 Unauthorized{"error_code": null, "message": "Invalid API key.", "request_id": null, "title": "Unauthorized"}