Migrating Speech-to-Text from Cartesia to Sarvam

View as Markdown

This guide covers migrating Cartesia 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, keep passing your audio file directly, and pick the right Sarvam API for the job: REST for short synchronous requests, Batch if you need diarization or very long audio, and WebSocket for real-time streaming.

Quick start

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

CartesiaSarvam
Base URLhttps://api.cartesia.aihttps://api.sarvam.ai
Auth headerAuthorization: Bearer <api_key>api-subscription-key: <api_key>
Auth failure status401403

With that, replace the transcription call:

1from cartesia import Cartesia
2
3client = Cartesia(api_key="YOUR_CARTESIA_API_KEY")
4
5with open("audio.mp3", "rb") as f:
6 transcription = client.stt.transcribe(
7 file=f,
8 model="ink-whisper",
9 language="en",
10 )
11
12print(transcription.text)

Cartesia’s /stt endpoint transcribes audio of any length synchronously, with no separate long-audio mode. Sarvam splits this by use case: REST for requests under 30 seconds, and Batch for longer audio or when you need speaker diarization, which Cartesia’s STT doesn’t offer at all.

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.

CartesiaSarvam
Transcription (any length, synchronous)POST /sttPOST /speech-to-text (up to 30 seconds per request; use Batch for longer audio)
Diarization or very long audioNot available as a separate modeBatch 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 /stt/turns/websocket (auto turn detection) or POST /stt/websocket (manual finalize)GET /speech-to-text/ws

Parameter concept map

CartesiaSarvamNote
model (ink-whisper for /stt; a separate ink-2 model for both real-time WebSocket endpoints)model (saaras:v3 everywhere)One model name across REST, Batch, and Streaming, rather than a different model for batch vs. real-time
file (audio file, any supported format)file (upload the audio file directly)Same concept; both take the file directly rather than a hosted URL
language (ISO-639-1, e.g. hi; optional, defaults to en)language_code (BCP-47, e.g. hi-IN; optional; pass unknown to auto-detect)Same idea; explicit BCP-47 region codes on Sarvam’s side (hi-IN rather than hi)
timestamp_granularities (["word"], REST only)Timestamps included automatically on Batch, at chunk (sentence/phrase) granularitySee the timestamps note below for the granularity difference
Response: text + words[] (word-level timing)Response: transcript + diarized_transcript.entries[] (Batch, with speaker_id and segment timing)Field names differ; see the timestamps note below

Sarvam’s Batch API returns timestamps per chunk (sentence or phrase segment), which is precise enough for subtitle alignment and audio navigation. If your Cartesia integration reads individual words[].start/end for word-by-word highlighting, adjust it to work at the chunk level instead, or use Sarvam’s REST/Streaming endpoints where per-word timing isn’t part of the response shape either.

Diarization and long-form audio

Cartesia’s /stt has no speaker-labeling mode to migrate from; this is new capability you get on Sarvam’s side, through the Batch API:

1from cartesia import Cartesia
2
3client = Cartesia(api_key="YOUR_CARTESIA_API_KEY")
4
5# Not available: Cartesia's STT has no diarization or speaker-labeling mode.
6# Multi-speaker audio transcribes as a single undifferentiated transcript.
7transcription = client.stt.transcribe(
8 file=open("meeting.mp3", "rb"),
9 model="ink-whisper",
10 language="en",
11)

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

Streaming

CartesiaSarvam
Turn-detection WebSocketPOST /stt/turns/websocket, model ink-2, tuned with turn_start_threshold, turn_eager_end_threshold, turn_end_threshold, turn_end_timeout_ms; streams turn.start/turn.update/turn.eager_end/turn.resume/turn.end eventsWebSocket API (GET /speech-to-text/ws) with high_vad_sensitivity and vad_signals enabled: one automatic “this turn just ended” signal, fewer thresholds to tune
Manual finalizePOST /stt/websocket, model ink-2 or ink-whisper; commit a segment with the plain-text message finalizeflush_signal plus an explicit ws.flush() call: force a transcript out on your own schedule instead of waiting for silence detection
Connection-time configmodel, language, audio encoding/sample ratemodel, mode, language_code, sample_rate, input_audio_codec

What’s different by design

  • Diarization comes standard on longer audio. Cartesia’s STT has no speaker-labeling mode; Sarvam’s Batch API identifies and labels speakers automatically with with_diarization and num_speakers, no separate product or add-on needed.
  • Flexible output modes on one model. saaras:v3 supports transcribe, translate, verbatim, translit, and codemix modes on the same endpoint, rather than transcription being the only option.
  • One model, one workflow. There’s no dated version pin to track, and no separate model to switch to for real-time use: saaras:v3 is the same model across REST, Batch, and Streaming.
  • 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 cartesia import Cartesia
2
3client = Cartesia(api_key="YOUR_CARTESIA_API_KEY")
4
5def transcribe(path: str) -> str:
6 with open(path, "rb") as f:
7 transcription = client.stt.transcribe(
8 file=f,
9 model="ink-whisper",
10 language="en",
11 )
12 return transcription.text

Common mistakes

It doesn’t, so there’s nothing to port over parameter-for-parameter. Add with_diarization=True and num_speakers directly on a Sarvam Batch job; this is new configuration, not a renamed existing one.

Sarvam’s REST endpoint caps at 30 seconds, unlike Cartesia’s /stt, which accepts audio of any length in one call. Route anything longer through the Batch API instead of expecting the REST endpoint to chunk it for you.

Batch API timestamps cover sentence or phrase chunks, not individual words, unlike Cartesia’s optional timestamp_granularities: ["word"]. If your integration builds word-by-word highlighting from words[].start/end, adjust it to work at the chunk level instead.

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

Cartesia’s /stt/turns/websocket only accepts ink-2, not the ink-whisper model used for batch requests. Sarvam has one model name, saaras:v3, to pass everywhere, so there’s no equivalent swap to remember on Sarvam’s side.

See also