Migrating Speech-to-Text from Gemini to Sarvam

View as Markdown

This guide covers migrating Gemini audio transcription calls to Sarvam. If you’re building a full voice agent with live, bidirectional audio rather than transcribing recorded audio 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: Gemini transcribes audio by passing it, along with a natural-language instruction, to a general model. Sarvam has a dedicated transcription endpoint. Swap the text-plus-audio input list for a transcribe() call with a mode parameter, and read the result from a guaranteed-clean transcript field instead of interaction.output_text.

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:

Gemini APISarvam
Base URLhttps://generativelanguage.googleapis.comhttps://api.sarvam.ai
Auth headerx-goog-api-key: <api_key>api-subscription-key: <api_key>
Auth failure status401403

With that, replace the transcription call:

1import base64
2from google import genai
3
4client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
5
6with open("audio.mp3", "rb") as f:
7 audio_bytes = f.read()
8
9interaction = client.interactions.create(
10 model="gemini-3.5-flash",
11 input=[
12 {"type": "text", "text": "Generate a transcript of the speech."},
13 {
14 "type": "audio",
15 "data": base64.b64encode(audio_bytes).decode("utf-8"),
16 "mime_type": "audio/mp3",
17 },
18 ],
19)
20
21print(interaction.output_text)

Gemini’s reply is a model’s response to your prompt, so its exact wording and formatting can vary between calls unless your prompt constrains it carefully. Sarvam’s response.transcript is a dedicated field that always contains just the transcript.

The rest of this page covers the full endpoint and parameter mapping, long audio, 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.

Gemini APISarvam
TranscriptionPOST /v1beta/interactions, with the audio passed as an input content block alongside a text prompt asking for a transcriptPOST /speech-to-text (up to 30 seconds per request; use Batch for longer audio)
Long audio (up to 9.5 hours per prompt)Upload via client.files.upload(...) first, then reference the file’s uri in the inputBatch API: POST /speech-to-text/job/v1 (create), upload, start, then poll or receive a webhook callback, up to 2 hours per file
Real-time streaming transcriptionNot applicable to this endpoint; live audio goes through the Live API insteadGET /speech-to-text/ws

Parameter concept map

Gemini APISarvamNote
model (a general model, e.g. gemini-3.5-flash, shared with text generation)model (saaras:v3, a dedicated speech-to-text model)A transcription-specific endpoint and model, rather than a general model prompted to behave like one
A natural-language prompt (e.g. {"type": "text", "text": "Generate a transcript of the speech."})mode="transcribe" (a dedicated parameter)An explicit parameter rather than an instruction the model has to interpret correctly every time
{"type": "audio", "data": ..., "mime_type": ...} input block (or "uri" for an uploaded file)file (upload the audio file directly)Same concept: the audio goes in as the file content either way
No dedicated language parameter; Gemini infers language from the audio and the promptlanguage_code (optional, BCP-47, e.g. hi-IN; pass unknown to auto-detect)Same auto-detect behavior available explicitly, plus the option to hint the language for better accuracy
Response: interaction.output_text (a model reply satisfying the prompt)Response: response.transcript (a dedicated, transcript-only field)Guaranteed to be just the transcript, not a model’s phrasing of one

Long audio and diarization

For files over 20 MB, upload first instead of inlining the audio.

1from google import genai
2
3client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
4
5uploaded_file = client.files.upload(file="long-meeting.mp3")
6
7interaction = client.interactions.create(
8 model="gemini-3.5-flash",
9 input=[
10 {"type": "text", "text": "Generate a transcript of the speech."},
11 {
12 "type": "audio",
13 "uri": uploaded_file.uri,
14 "mime_type": uploaded_file.mime_type,
15 },
16 ],
17)
18
19print(interaction.output_text)

Sarvam’s REST endpoint caps at 30 seconds of audio regardless of file size. For a file like this one, use the Batch API instead, shown next, rather than the direct transcribe() call above.

Move diarized or long-form audio to the Batch API.

1from google import genai
2
3client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
4
5# Not available as a dedicated mode: Gemini has no speaker-labeling parameter.
6# You'd need to prompt the model to attempt speaker labeling in its reply,
7# with no guarantee of a consistent, structured format back.
8uploaded_file = client.files.upload(file="meeting.mp3")
9
10interaction = client.interactions.create(
11 model="gemini-3.5-flash",
12 input=[
13 {"type": "text", "text": "Transcribe this and label each speaker."},
14 {
15 "type": "audio",
16 "uri": uploaded_file.uri,
17 "mime_type": uploaded_file.mime_type,
18 },
19 ],
20)

See the Batch API guide for webhook setup and its Speaker Diarization section for the current per-job speaker limit.

What’s different by design

  • A dedicated transcription endpoint. POST /speech-to-text and mode="transcribe" are purpose-built for turning audio into text, rather than a general model interpreting a natural-language request.
  • A guaranteed-clean transcript field. response.transcript always holds just the transcript, with no prompt-engineering needed to keep a general-purpose model from adding commentary or reformatting the text.
  • Flexible output modes on one model. saaras:v3 also supports translate, verbatim, translit, and codemix modes as explicit parameters, rather than needing a differently worded prompt for each.
  • Diarization comes standard, not prompted for. Sarvam’s Batch API identifies and labels speakers with with_diarization and num_speakers, returning a structured diarized_transcript, rather than relying on a model’s best-effort attempt inside free-text output.
  • Indian language depth is Sarvam’s specific strength. Saaras v3 covers 22 Indian languages, including Assamese, Konkani, Kashmiri, Sindhi, Sanskrit, Santali, Manipuri, Bodo, Maithili, and Dogri, with native handling for code-mixed speech like Hinglish.

Full example

A small wrapper function, before and after, showing the complete set of changes together:

1import base64
2from google import genai
3
4client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
5
6def transcribe(path: str) -> str:
7 with open(path, "rb") as f:
8 audio_bytes = f.read()
9 interaction = client.interactions.create(
10 model="gemini-3.5-flash",
11 input=[
12 {"type": "text", "text": "Generate a transcript of the speech."},
13 {
14 "type": "audio",
15 "data": base64.b64encode(audio_bytes).decode("utf-8"),
16 "mime_type": "audio/mp3",
17 },
18 ],
19 )
20 return interaction.output_text

Common mistakes

It’s a model’s reply to your prompt, so it can include extra phrasing, partial answers, or formatting depending on how the prompt was worded. Read response.transcript on Sarvam’s side instead, which is always transcript-only.

Sarvam’s mode parameter (transcribe, translate, verbatim, translit, codemix) replaces the need to phrase a natural-language instruction carefully and hope the model interprets it the same way every time.

Asking Gemini to “label each speaker” in the prompt returns free text, not a structured, per-segment breakdown. Sarvam’s with_diarization and num_speakers are Batch API parameters that return a dedicated diarized_transcript field, so route diarized requests through the Batch workflow instead of relying on prompt wording.

Calling the Batch endpoints directly over HTTP (rather than through the SDK) requires nesting parameters under job_parameters, e.g. {"job_parameters": {"model": "saaras:v3", ...}}. A flat body won’t be picked up.

See also