Skip to main content

Overview

Stream raw PCM audio frames and receive transcript segments as speech is detected. The server uses Voice Activity Detection (VAD) to identify speech boundaries and returns a transcript for each segment.

Endpoint


Connection Headers

All configuration is passed as WebSocket upgrade headers at connection time. Most headers are fixed for the life of the connection — reconnect with new headers to change them. The exceptions are x-enable-substitution, x-substitution-map, and the word-boosting headers (x-bias-list, x-bias-score), which can also be updated mid-session via update_settings control frames. Choosing the right sample rate:

VAD tuning headers

These five optional headers control where speech starts and stops. Defaults are tuned for a clean wideband microphone — override them for telephony, noisy environments, or latency-sensitive flows. See VAD Tuning for guidance and ready-made presets.

Voice Activity Detection (VAD) Tuning

VAD is what turns a continuous audio stream into discrete utterances. It listens for the moment speech starts, holds the segment open through natural pauses, and closes it when the speaker stops. Every closed segment becomes exactly one transcript message — so VAD settings directly control both how your transcripts are chunked and how quickly they arrive.
VAD headers are fixed for the life of the connection. To change them, close the WebSocket and reconnect with the new values.

The two knobs that matter most

Most tuning comes down to two trade-offs:

Header reference

x-vad-threshold

How confident the model must be that a frame contains speech. Type float, default 0.7, range 0.0–1.0.
  • Higher (0.75+) — stricter. Rejects background noise, hold music, and line hiss, but may miss soft or distant speech.
  • Lower (0.45–0.55) — more sensitive. Catches quiet speakers and whispers, but noise is more likely to open a segment.

x-min-silence-ms

How much silence must follow speech before the segment is closed and transcribed. Type integer, default 500, range 100–2000. This is the single biggest lever on end-of-utterance latency — the transcript cannot arrive until this timer expires.
  • Lower (300–400) — snappier results. Best for IVR prompts and short commands, but a speaker who pauses mid-sentence (“my number is… 98765…”) will get two transcripts instead of one.
  • Higher (700–900) — more complete utterances. Best for natural conversation and dictation, at the cost of ~half a second of extra latency per turn.

x-min-speech-ms

How much consecutive speech is needed before a segment is opened. Type integer, default 100 (200 when x-sample-rate: 8000), range 50–2000. Acts as a noise gate at the start of speech. A door slam, a click, or a cough is shorter than this window, so it never opens a segment and never costs you a transcription.
  • Raise (200–300) in noisy environments or on noisy telephony lines.
  • Lower (50–80) only if you are losing very short interjections like “haan” or “yes”.

x-min-segment-ms

A floor on the amount of speech required to confirm a segment. Type integer, default 120, range 100–1000. This works together with x-min-speech-ms — whichever demands more speech wins. Because it applies at the start of a segment, audio below this threshold never opens a segment at all, so no transcript message is produced for it.
  • Raise to 200–300 to suppress residual coughs, breaths, and line pops that survive the x-min-speech-ms gate.
  • Leave at the default (or lower toward 100) if you expect genuine one-word answers like “yes”, “नहीं”, or “two”.
Raising x-min-segment-ms too far silently drops short answers — they produce no transcript message at all. If users report that “yes”/“no” responses never come back, this header is the first thing to check.

x-max-speech-s

Hard cap on how long a single segment can run. Type float, disabled by default, range 5.0–120.0. Normally a segment closes when the speaker pauses. If someone talks continuously — reading an address, giving a monologue — that pause may never come, and you get no transcript until they stop. Setting this header force-closes and transcribes the segment at the cap so results keep flowing.
  • Leave unset for normal turn-taking conversation.
  • Set to 20–30 for monologue-heavy audio (voicemail, dictation, long narration) to get steady intermediate results.
The overall session is always capped at 15 minutes regardless of this setting. x-max-speech-s bounds a single uninterrupted segment within that session.

Presets

Copy-paste starting points. Tune from here rather than from scratch.

Setting VAD headers in code

VAD headers are sent at connection time, alongside your API key.
Browser WebSocket cannot set custom headers. From a browser, proxy the connection through your own backend and attach the headers there — this also keeps your API key off the client.
The Python SDK (gnani-vachana) does not currently expose VAD tuning. Use a raw WebSocket client, as above, if you need these headers.

Connection Flow

A WebSocket session follows a strict sequence:
  1. Client connects — opens a WebSocket to /stt/v3/stream with all required headers.
  2. Server confirms — immediately sends a connected message echoing the active configuration.
  3. Client streams audio — continuously sends binary frames of raw PCM audio at a steady real-time cadence.
  4. Server detects speech — VAD identifies end-of-speech boundaries and emits a processing message to acknowledge that a segment was captured.
  5. Server returns transcript — sends a transcript message with the transcribed text, segment metadata, and latency.
  6. Either side closes — client or server may close the connection at any time.
The processing message is a low-latency signal that audio was captured and transcription has begun. Expect a transcript message shortly after.

Audio Format & Sending Audio

All audio must be sent as raw PCM binary frames over the WebSocket. No container format (WAV, MP3, etc.) is accepted mid-stream.

PCM Specification

Sending Rules

  • Each binary frame must be exactly 1,024 bytes.
  • Frames must be sent at real-time cadence — one frame every 32 ms (16 kHz) or 64 ms (8 kHz). Do not buffer and burst; this degrades VAD accuracy.
  • For 44100 and 48000 Hz sources, the server resamples internally — still send 1,024-byte frames at the appropriate cadence.

Server Messages

The server sends JSON text frames. All messages share a type discriminator field and an ISO-8601 timestamp.

connected

Sent once immediately after the WebSocket handshake succeeds.

processing

Emitted when VAD detects the end of a speech segment and transcription has begun. Use this as a low-latency acknowledgment that audio was captured.

transcript

Contains the transcribed text for a completed speech segment.

error

Sent when the server encounters a recoverable or fatal error. The connection may remain open after a recoverable error.

settings_updated

Sent in response to a successful update_settings control frame. Echoes the full active configuration after the update.

Client Control Frames

Alongside binary PCM audio frames, the client can send JSON text frames to update settings mid-session. No reconnect needed.

update_settings

Send a JSON text frame with "type": "update_settings" and the settings you want to change. Unspecified fields keep their current values.

Substitution key

You can send enabled and map together or separately:

Bias key

Omit a key to keep its current value — for example {"bias": {"score": 2.0}} changes only the strength.
The change applies from the next speech segment. An invalid update is ignored as a whole and the previous settings stay in effect. The server replies with a settings_updated frame confirming the new state.

Python SDK

The official Python SDK wraps the WebSocket connection, audio pacing, and event parsing into a clean async interface.

Installation

Requires Python 3.10+.

Authentication

The streaming client requires your API key and language code.

Stream Audio from a File

Use the async context manager and the stream_audio helper. It handles real-time pacing automatically so frames are sent at the correct cadence for VAD.

Iterate Over Events Manually

For lower-level control — handling each event type differently or interleaving sending and receiving — iterate over the stream directly.

Using 8 kHz Audio (Telephony)

SDK Event Types

All events are typed dataclasses with a raw field containing the full server JSON.

Error Handling


Supported Languages


Inverse Text Normalization (ITN)

When x-format: transcribe is set, ITN runs on every transcript segment immediately after recognition — converting spoken-form numbers, currency, dates, times, and phone numbers into the compact written form a reader expects.

What ITN Normalizes

1 — Cardinal & Ordinal Numbers

2 — Currency & Money

3 — Dates

4 — Times

Indian time-of-day words (सुबह, दोपहर, शाम, रात) automatically map to 24-hour HH:MM output.

5 — Phone Numbers & PIN Codes

6 — Mixed & Code-Mixed Utterances

Native Script Digits — itn_native_numerals

By default, ITN outputs Western Arabic digits (0–9). Set itn_native_numerals: true in the connection headers to render digits in the native script of the target language.

What ITN Does Not Change

ITN intentionally preserves idiomatic and ambiguous phrases to avoid incorrect normalization.
  • दो तीन (meaning a few) stays as text, not 2 or 3
  • कर दो / ले दो (imperative verbs) are kept as words, not treated as cardinal 2

Find-and-Replace (Substitution)

After transcription (and after ITN, if enabled), the server can apply a custom set of find-and-replace rules to every transcript segment before delivering it to your client. Use this to:
  • Normalise domain-specific terms (“rs” → “INR”, “ok” → “acknowledged”)
  • Replace competitor names, internal codes, or product shortforms
  • Standardise phrases the model transcribes differently from your expected output

Enable at connection time

Pass x-enable-substitution: true and your rules in x-substitution-map as part of the WebSocket upgrade headers.

Toggle mid-session

Send an update_settings control frame at any time to turn substitution on/off or swap the rule set — no reconnect needed.
See Client Control Frames for the full reference.

Rule format

  • Maximum 10 rules per session.
  • Matching is case-insensitive and respects Unicode word boundaries — "ok" matches "OK" but not "okay".
  • Longer phrases take priority — "New Delhi" is tried before "Delhi".
  • Rules are applied in a single pass with no cascading.
Substitution runs on the final client-visible transcript only. Billing records always carry the original raw transcript.

Word Boosting

Word boosting nudges the recognizer toward specific words — brand names, product names, domain terms — so they are more likely to be transcribed correctly. It affects recognition itself and is separate from find-and-replace.

Enable at connection time

Send x-bias-list and x-bias-score in the WebSocket upgrade headers. Both are needed for boosting to take effect.
The list and score apply to every transcript segment in the session. If x-partials is on, they also apply to partial_transcript results.

Change it mid-session

Send an update_settings frame to replace the words or strength without reconnecting — see Client Control Frames.

Rules

Invalid word-boosting input never rejects the connection or returns an error. If any entry breaks the rules, or the score is not an allowed value, boosting is skipped and audio is transcribed normally. Mid-session, an invalid update_settings is ignored and the previous list stays active.

Indic words in a header

HTTP headers are bytes, and many WebSocket clients refuse or garble non-ASCII values. The portable way is a JSON array with \u escapes, which is plain ASCII:
Raw UTF-8 header bytes are also accepted if your client can send them.
Word boosting is also supported on /stt/v4/stream via the same headers, but that route has no update_settings channel for changing it mid-session.