Other clients
ElevenLabs API drop-in
ElevenLabs' own WebSockets, REST and voices APIs through the cache: keep the ElevenLabs SDK, change the URL and key.
ElevenLabs' Python SDK#
ElevenLabs’ API, mirrored under https://api.ttscacherouter.dev/elevenlabs. Keep the ElevenLabs SDK: pass the router as its base_url and your router API key as its api_key.
import osfrom elevenlabs.client import ElevenLabsclient = ElevenLabs( base_url="https://api.ttscacherouter.dev/elevenlabs", api_key=os.environ["TTS_CACHE_ROUTER_API_KEY"],)audio = client.text_to_speech.convert( "JBFqnCBsd6RMkjVDRZzb", text="Your order has shipped. It arrives on Friday.", model_id="eleven_flash_v2_5", output_format="mp3_22050_32",)with open("speech.mp3", "wb") as f: f.writelines(audio)convert, stream, convert_with_timestamps, stream_with_timestamps and convert_realtime work through the router. Requests, responses, messages, status codes and error bodies are ElevenLabs’ own; your query parameters, settings and body are forwarded as you sent them, with the ElevenLabs key stored under Provider keys. For a LiveKit agent, see ElevenLabs with LiveKit.
WebSockets#
/elevenlabs/v1/text-to-speech/{voice_id}/multi-stream-input: contexts by id, as LiveKit’s plugin uses it./elevenlabs/v1/text-to-speech/{voice_id}/stream-input: one stream per connection, as the SDK’sconvert_realtimeuses it./elevenlabs/v1/text-to-dialogue/multi-stream-input:eleven_v3andeleven_v4models.
wss://api.ttscacherouter.dev/elevenlabs/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb/multi-stream-input?model_id=eleven_flash_v2_5&output_format=pcm_24000&auto_mode=true&sync_alignment=trueAudio messages carry the same timings (alignment, normalizedAlignment) a direct call gets, from the cache too, and each context ends with its isFinal (is_final on dialogue) once you close it. Every output format works, opus included. In MP3, a context’s later sentences carry no ID3 tag, as one direct stream carries only one.
REST and voices#
POST /elevenlabs/v1/text-to-speech/{voice_id}and…/stream: the audio in the requestedoutput_format.POST /elevenlabs/v1/text-to-speech/{voice_id}/with-timestampsand…/stream/with-timestamps: audio with character timings, as JSON.POST /elevenlabs/v1/text-to-dialogue/stream: dialogue models.GET /elevenlabs/v1/voices: your voices, never cached.
Any other path under /elevenlabs answers 404.
curl -N "https://api.ttscacherouter.dev/elevenlabs/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb/stream?output_format=mp3_22050_32" \ -H "xi-api-key: $TTS_CACHE_ROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Your order has shipped. It arrives on Friday.", "model_id": "eleven_flash_v2_5"}' \ --output speech.mp3curl "https://api.ttscacherouter.dev/elevenlabs/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb/with-timestamps?output_format=pcm_16000" \ -H "xi-api-key: $TTS_CACHE_ROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Your order has shipped.", "model_id": "eleven_flash_v2_5"}'curl https://api.ttscacherouter.dev/elevenlabs/v1/voices -H "xi-api-key: $TTS_CACHE_ROUTER_API_KEY"What is cached#
- WebSockets: each sentence. With
auto_mode=true(LiveKit’s default) each text message you send is synthesized on its own, exactly as ElevenLabs does. Without it, ElevenLabs groups buffered text into chunks of its own choosing; the router instead splits it into sentences with LiveKit’s BlingFire tokenizer and synthesizes each once it is complete, so the audio can be grouped slightly differently from a direct call.flushends the sentence in progress. - REST: each request as a whole. The same query and body again replays the same response.
- A voice’s stored settings (used when
voice_settingsis left out or partial) and a pronunciation dictionary named without aversion_idcan change in ElevenLabs, so the router re-reads them about once a minute: after a change, new sentences are synthesized and cached afresh within about a minute.
Errors#
A wrong router key gets ElevenLabs’ own refusal: HTTP 400 invalid_api_key (401 when no key is sent), and on a WebSocket the same error message followed by a close with code 1008. So does an org with no ElevenLabs key stored, with a message that links to the dashboard. Errors from ElevenLabs, such as an unknown voice, reach you as ElevenLabs sent them.
HTTP/1.1 400 Bad Request{"detail": {"type": "authentication_error", "code": "invalid_api_key", "message": "API key is invalid.", "status": "invalid_api_key", "request_id": null, "param": "api_key"}}{"message": "API key is invalid.", "error": "invalid_api_key", "code": 1008}(close 1008)