Migrating Speech-to-Text from Deepgram to Sarvam

View as Markdown

This guide covers migrating Deepgram speech-to-text (transcription) API calls to Sarvam. If you’re building a full voice agent rather than transcribing audio directly, 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: swap model for model, read the transcript from a flatter response shape, and pick the right Sarvam API for the job: REST for short synchronous requests, Batch for diarized or very long audio, and WebSocket for real-time streaming.

Quick start

Base URL and auth header change the same way for every Deepgram migration, so they’re folded in here rather than in a separate page:

DeepgramSarvam
Base URLhttps://api.deepgram.comhttps://api.sarvam.ai
Auth headerAuthorization: Token <api_key>api-subscription-key: <api_key>
Auth failure status401403

With that, replace the transcription call:

1from deepgram import DeepgramClient
2
3client = DeepgramClient(api_key="YOUR_DEEPGRAM_API_KEY")
4
5with open("audio.wav", "rb") as f:
6 response = client.listen.v1.media.transcribe_file(
7 request=f.read(),
8 model="nova-3",
9 language="en",
10 )
11
12print(response.results.channels[0].alternatives[0].transcript)

The rest of this page covers the full endpoint and parameter mapping, diarization via the Batch API, 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.

DeepgramSarvam
Synchronous transcriptionPOST /v1/listenPOST /speech-to-text (up to 30 seconds per request; use Batch for longer audio)
Diarized or very long audioSame POST /v1/listen call, with diarize_model setBatch 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 transcriptionPOST /v1/listen (WebSocket upgrade)GET /speech-to-text/ws

Parameter concept map

DeepgramSarvamNote
model (nova-3, nova-2, and other version/domain variants)model (saaras:v3)One current model to pass, not a family of version and domain-specific variants
request (raw audio bytes, e.g. open(...).read())file (an open file object)Same idea; Sarvam takes the file handle directly rather than pre-reading it into bytes
language (BCP-47, e.g. en; optional)language_code (BCP-47, e.g. en-IN; optional; pass unknown to auto-detect)Same concept, region-qualified codes on Sarvam’s side
diarize_model (enables diarization on the same synchronous call)with_diarization + num_speakers (Batch API only)Same concept; on Sarvam, diarization is a Batch job parameter rather than a flag on the fast synchronous endpoint
smart_format / punctuate / numerals (formatting toggles)Handled automaticallySarvam applies sensible formatting by default, so there’s no separate set of toggles to configure
Response: results.channels[0].alternatives[0].transcript + .words[]Response: transcript + diarized_transcript.entries[] (Batch, with speaker_id and segment timing)Flatter response shape; no channel/alternative nesting to walk through

Deepgram’s response also supports optional sentiment, topic, intent, and summary detection, and content redaction, layered on top of the transcript. Sarvam’s Speech-to-Text focuses on transcription accuracy, output modes (transcribe, translate, verbatim, translit, codemix), and Indian-language coverage; if you need that kind of downstream text analysis, run Sarvam’s transcript through Chat Completion as a second step.

Diarization and long-form audio

Deepgram enables diarization on the same synchronous call via diarize_model; Sarvam’s Batch API handles both diarization and long-form audio instead:

1from deepgram import DeepgramClient
2
3client = DeepgramClient(api_key="YOUR_DEEPGRAM_API_KEY")
4
5with open("meeting.wav", "rb") as f:
6 response = client.listen.v1.media.transcribe_file(
7 request=f.read(),
8 model="nova-3",
9 language="en",
10 diarize_model="latest",
11 )

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

Streaming

DeepgramSarvam
Connection configWebSocket upgrade of /v1/listen: model, language, encoding, sample_rate, interim_results, endpointingWebSocket API (GET /speech-to-text/ws): model, mode, language_code, sample_rate
Server messagesResults, Metadata, UtteranceEnd, SpeechStartedTranscript for your chosen mode directly, plus START_SPEECH/END_SPEECH events when vad_signals=true
Turn-end detectionendpointing (silence duration before finalizing) + vad_eventshigh_vad_sensitivity + vad_signals: one automatic “this turn just ended” signal
Partial transcriptsinterim_results flagNo separate flag; returns the transcript for your chosen mode directly as each segment finalizes
Finalize / close{"type": "Finalize"} / {"type": "CloseStream"}flush_signal plus an explicit ws.flush() call / simply closing the connection

Deepgram also offers Flux (flux-general-en/flux-general-multi), a separate GET /v2/listen endpoint purpose-built for conversational turn detection, with its own tuning params (eager_eot_threshold, eot_threshold). Sarvam’s turn-detection features (high_vad_sensitivity, vad_signals) live in the same WebSocket API used for regular transcription, so there’s no separate model or endpoint to choose between for conversational use.

What’s different by design

  • One current model, not a version-and-domain matrix. Deepgram’s nova-3, nova-2-medical, nova-2-finance, and similar domain-specific variants become a single saaras:v3, used the same way across REST, Batch, and Streaming.
  • A flatter response shape. response.transcript reads directly, rather than walking results.channels[0].alternatives[0].transcript.
  • Diarization and long-form audio share one workflow. The Batch API handles speaker diarization, files up to 2 hours, and multiple files per job in a single job-based flow, with the same with_diarization/num_speakers parameters either way.
  • Flexible output modes on one model. saaras:v3 supports transcribe, translate, verbatim, translit, and codemix modes on the same endpoint.
  • 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:

1from deepgram import DeepgramClient
2
3client = DeepgramClient(api_key="YOUR_DEEPGRAM_API_KEY")
4
5def transcribe(path: str) -> str:
6 with open(path, "rb") as f:
7 response = client.listen.v1.media.transcribe_file(
8 request=f.read(),
9 model="nova-3",
10 language="en",
11 )
12 return response.results.channels[0].alternatives[0].transcript

Common mistakes

Sarvam’s file parameter takes the open file object directly (file=open(path, "rb")), unlike Deepgram’s request, which expects the bytes already read out (request=f.read()). Passing a file handle where Deepgram’s SDK expects bytes, or vice versa, is the most common porting slip.

Deepgram enables diarization on the same synchronous call via diarize_model. Sarvam’s with_diarization is a Batch API parameter instead, so diarized requests need to go through the Batch workflow even for a short file.

Sarvam’s response is flat: response.transcript, not response.results.channels[0].alternatives[0].transcript. Update any code that destructures the Deepgram response shape.

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 modeled on Deepgram’s request shape won’t be picked up.

See also