Migrating Text-to-Speech from Gemini to Sarvam
Migrating Text-to-Speech from Gemini to Sarvam
This guide covers migrating Gemini text-to-speech calls to Sarvam. If you’re building a full voice agent with live, bidirectional audio rather than generating speech from text one call at a time, start from the LiveKit or Pipecat voice agent build guides instead: they aren’t a 1:1 API swap and aren’t covered here.
In short: replace generation_config.speech_config (a list of {voice, speaker, language} objects) plus response_format: {"type": "audio"} with two flat params, speaker and target_language_code, and read the audio back from a plain audios array instead of unwrapping interaction.output_audio.data.
Quick start
Base URL and auth header change the same way for every Gemini migration, so they’re folded in here rather than in a separate page:
With that, replace the synthesis call and add the required language code:
Before (Gemini)
After (Sarvam)
Gemini’s output_audio.data is raw PCM you wrap in a WAV header yourself. Sarvam’s convert() returns a JSON object; the audio lives in response.audios as a list of base64-encoded, already-WAV-formatted strings. Join and decode before writing to disk, or use the Python SDK’s sarvamai.play.save() helper, which does this for you.
The REST response is JSON with a base64 audios array, already WAV-formatted; no manual header construction needed. Decode .audios[0] before writing it out; see the REST API guide for the full decode snippet.
The rest of this page covers the full endpoint and parameter mapping, streaming, and common mistakes.
Endpoint map
The tables below aren’t a checklist to apply field-by-field: Quick Start above already shows the whole call swapped over in one go. Use these tables as a reference for what changed and why, not as a sequence of edits to make yourself.
Parameter concept map
Streaming
What’s different by design
- One flat call, no list to build.
speakerandtarget_language_codeare passed directly, rather than nested inside ageneration_config.speech_configlist, even for a single voice. - A dedicated TTS endpoint.
POST /text-to-speechis purpose-built for speech synthesis, rather than a general interactions endpoint configured to return audio instead of text. - Ready-to-use WAV output.
response.audiosis already WAV-formatted; there’s no separate step to wrap raw PCM samples in a WAV header yourself. - Explicit language targeting. Every Sarvam request carries
target_language_code, so pronunciation and prosody are correct by design on every call, especially valuable for Hindi, Hinglish, and other code-mixed Indian-language text. - Indian language depth is Sarvam’s specific strength. Sarvam covers Hindi plus 9 other Indian languages (Bengali, Tamil, Telugu, Kannada, Malayalam, Marathi, Gujarati, Punjabi, Odia), natively tuned for code-mixed input like Hinglish.
Full example
A small wrapper function, before and after, showing the complete set of changes together:
Before (Gemini)
After (Sarvam)
Common mistakes
Writing raw PCM bytes as if they were a finished WAV file
Gemini’s TTS output is unwrapped PCM; without the wave module step, the resulting file has no valid header and won’t play. Sarvam’s response.audios entries are already complete WAV files once base64-decoded, so this extra wrapping step goes away entirely, not just changes shape.
Forgetting target_language_code
It’s required on every request, by design: this is what guarantees Sarvam applies the correct pronunciation and prosody instead of relying on the optional, per-entry language field Gemini lets you set inside speech_config. Include it from the start rather than treating it as optional.
Looking for a multi-speaker speech_config equivalent
There isn’t a single-call two-speaker mode. Generate each speaker’s dialogue as a separate convert() call with its own speaker, then stitch the resulting audio together in your application code.
Reusing a chat model name for TTS
Gemini’s TTS uses a distinct model family (e.g. gemini-3.1-flash-tts-preview) from its chat models. Likewise, Sarvam’s model here is bulbul:v2/bulbul:v3, not sarvam-30b/sarvam-105b; the chat and speech models aren’t interchangeable on either platform.
Not retrying on Gemini's audio-token failure mode
Google’s own docs note that Gemini’s TTS models can occasionally emit text tokens instead of audio, which fails the request with a 500 error; they recommend building retry logic for it. Sarvam’s TTS doesn’t have that particular failure mode, so the retry logic in the examples on this page (403/429/503) is the only error handling you need to carry over.
See also
- Migration overview: base URL, auth, and SDK client changes
- Migrating Chat Completion
- Migrating Speech-to-Text
- Which Text-to-Speech API to Use
- TTS Best Practices
- Voices
- Errors & Troubleshooting