Other clients
Sarvam API drop-in
Sarvam's own REST, HTTP stream and WebSocket APIs through the cache: keep the Sarvam SDK, change the URL and key.
Sarvam's Python SDK#
Sarvam’s API, mirrored under https://api.ttscacherouter.dev/sarvam. Keep your Sarvam client: give it the router as its environment and your router API key as its api_subscription_key.
import base64import osfrom sarvamai import SarvamAIfrom sarvamai.environment import SarvamAIEnvironmentclient = SarvamAI( api_subscription_key=os.environ["TTS_CACHE_ROUTER_API_KEY"], environment=SarvamAIEnvironment( base="https://api.ttscacherouter.dev/sarvam", production="wss://api.ttscacherouter.dev/sarvam", creative="https://api.ttscacherouter.dev/sarvam/dubbing", # unused: the router serves text-to-speech only ),)response = client.text_to_speech.convert( text="नमस्ते, मैं प्रिया बोल रही हूँ।", language_code="hi-IN", speaker="shubh", model="bulbul:v3",)with open("speech.wav", "wb") as f: f.write(base64.b64decode(response.audios[0]))import asyncioimport base64import osfrom sarvamai import AsyncSarvamAIfrom sarvamai.environment import SarvamAIEnvironmentHTTP = "https://api.ttscacherouter.dev/sarvam"async def main() -> None: client = AsyncSarvamAI( api_subscription_key=os.environ["TTS_CACHE_ROUTER_API_KEY"], environment=SarvamAIEnvironment(base=HTTP, production="wss://api.ttscacherouter.dev/sarvam", creative=f"{HTTP}/dubbing"), ) pcm = b"" async with client.text_to_speech_streaming.connect(model="bulbul:v3", send_completion_event=True) as ws: await ws.configure(target_language_code="hi-IN", speaker="shubh", output_audio_codec="linear16") await ws.convert("नमस्ते, मैं प्रिया बोल रही हूँ।") await ws.flush() async for message in ws: if message.type == "audio": pcm += base64.b64decode(message.data.audio) elif message.type == "event" and message.data.event_type == "final": break print(f"{len(pcm)} bytes of 16-bit PCM")asyncio.run(main())convert, convert_stream and text_to_speech_streaming.connect work through the router. Requests, responses, status codes, error bodies and WebSocket messages are Sarvam’s own; your request is forwarded to Sarvam as you sent it, with the Sarvam key stored under Provider keys. For a LiveKit agent, see Sarvam with LiveKit.
WebSocket#
wss://api.ttscacherouter.dev/sarvam/text-to-speech/ws, and /ws/v2, which LiveKit’s Sarvam plugin uses for bulbul:v4-flash.
Sarvam’s model and send_completion_event query parameters and its config, text, flush and ping messages. The router splits your text into sentences and synthesizes each one on its own, so each sentence your org has synthesized before with the same config plays from the cache.
- As with Sarvam, text that ends a sentence is spoken without waiting for
flush, and onefinalevent follows eachflush, after all its audio (when you setsend_completion_event=true). - A sentence with no letter or digit (an emoji on its own) is skipped, since Sarvam refuses it, and so is such a run at the end of a sentence (
…shipped it. 🚀), which Sarvam would refuse along with the sentence. - Every codec works,
wav,flacandopusincluded, message for message as Sarvam sends them. - A connection with no messages for 60 seconds is closed with Sarvam’s own idle error.
REST and HTTP stream#
POST https://api.ttscacherouter.dev/sarvam/text-to-speech and POST https://api.ttscacherouter.dev/sarvam/text-to-speech/stream, with your router API key in api-subscription-key.
curl https://api.ttscacherouter.dev/sarvam/text-to-speech/stream \ -H "api-subscription-key: $TTS_CACHE_ROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "नमस्ते, मैं प्रिया बोल रही हूँ।", "language_code": "hi-IN", "speaker": "shubh", "model": "bulbul:v3", "output_audio_codec": "mp3"}' \ --output speech.mp3curl https://api.ttscacherouter.dev/sarvam/text-to-speech \ -H "api-subscription-key: $TTS_CACHE_ROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "नमस्ते, मैं प्रिया बोल रही हूँ।", "language_code": "hi-IN", "speaker": "shubh", "model": "bulbul:v3"}'Each request is cached as a whole: the same body again replays the same audio. A REST reply from the cache has "request_id": null. Only text-to-speech is served: any other path under /sarvam answers 404.
Errors#
Errors arrive as Sarvam sends them, so your client's error handling keeps working.
- A wrong or missing router API key gets Sarvam’s own refusal, HTTP 403 (at the WebSocket handshake too). So does an org with no Sarvam key stored, with a message that links to the dashboard.
- Errors from Sarvam, such as a wrong stored Sarvam key or an unknown speaker, reach you as Sarvam sent them.
- When Sarvam reports an error and still speaks (a
dict_idit can’t find, for example), you get the error, the audio and thefinalas Sarvam sends them, and that audio isn’t cached.
HTTP/1.1 403 Forbidden{"error": {"message": "Invalid or missing authentication credentials", "code": "invalid_api_key_error", "request_id": null}}