STT WebSocket API
Real-time Speech-to-Text transcription via WebSocket streaming with support for 39 languages (including code-switched Indic+English) and telephony integration. Powered by 60db STT v01 (a non-hallucinating, multi-backend speech recognition stack).🚀 Quick Start (Copy & Paste)
- ✅ Authenticated
- ✅ Ready! Send audio now
- 📝 Hello world (transcribed text)
- ✅ Complete! Cost: $0.000043
📖 How It Works (5 Simple Steps)
- Connect with your API key
- Send
{ type: "start", ... }to begin session - Stream audio data (binary chunks)
- Receive text transcriptions in real-time
- Stop with
{ type: "stop" }when done
Endpoint
Authentication
Query parameter authentication: Example:Connection Details
Session Lifecycle
transcription with is_final: true, and speech_final mirrors is_final (there is no separate “canonical” phase and no LLM rewriting — see §Text is never rewritten). A single caller turn can still produce more than one final:
- a short pause can split a sentence into two finals (with
early_final: true); - a monologue longer than
max_utterance_secemits one or moresegment_final: truechunks, then a normal final.
final_id. A turn that contained no words ends with a terminal no_speech instead.
Text is never rewritten
After recognition, the server never rewrites the script or re-guesses the language, and there is no LLM refinement pass. The only post-recognition change is strict keyword/term spelling correction fromkeywords / context.terms (force tuner → Fortuner). keywords correct near-miss spellings in place; word timings are preserved. Everything else in text is exactly what the recognizer produced.
Client → Server Messages
start — Begin session
Sent once after connection is established. Must be sent before any audio.
Turn-ending profiles
early_final and turn_detector together decide how fast a turn closes and whether a sentence with a mid-pause stays in one final. Pick one profile:
Profile A is best for voicebots where a cut-off sentence causes a wrong answer. Profiles B and C are faster; join split finals in your middle layer if you use them. No words are lost with any profile — only where turns are cut changes. Confirm the effective values in
session_started.
audio — JSON audio chunk (browser mode)
Binary frame — raw μ-law audio (telephony mode)
Send a raw WebSocket binary frame with μ-law bytes, no JSON wrapper. The server auto-detects this as telephony mode on the first binary frame.config — Change settings mid-session
"languages": null to revert to auto-detect. Most changes are acknowledged with a *_changed event: language_changed, turn_detector_changed, utterance_end_changed, interim_frequency_changed, interim_delta_streaming_changed, token_streaming_changed, max_utterance_changed, audio_enhancement_changed, audio_format_changed, keywords_changed, context_changed. If any value is invalid you get an error and nothing in that message is applied. diarize, min_speakers and max_speakers can only be set in start. Changing languages mid-sentence first finalizes the current sentence, then switches — prefer switching between turns.
finalize — End the current turn now
transcription (or no_speech), or nothing if no audio is buffered. The session continues. Use it when your application already knows the caller is done — e.g. after a DTMF key press.
stop — End session
session_stopped, then closes.
test — Ping / latency check
test_response with the same timestamp for round-trip measurement.
Server → Client Messages
connecting — Authentication in progress
connection_established — Authentication successful
string
Service name:
"stt"integer
Your user ID
number
Available credits
string
Workspace name
connected — After start message is processed
Expect two
connected frames: the proxy’s own handshake (session_id plus a small server_info), then the upstream capability frame shown above. Neither means “audio is accepted” — wait for session_started. Keep any connected handler idempotent so audio capture does not start twice.session_started — start accepted, audio is now allowed
early_final/turn_detector here to confirm which profile the session is running. interim_frequency is the effective interim_results_frequency (null = off); interim_min_voiced_ms: null means the default (600). ignored_config_keys lists every key the server did not recognise (e.g. a browser echoCancellation) — unknown keys never fail a session. Send audio only after this event.
speech_started — VAD detected voice activity
transcription — Transcription result
All results (interim and final) share the same transcription type — differentiate with flags.
Final result (is_final=true, speech_final=true):
segment_final=true) — max_utterance_sec cut a long monologue; the same turn continues, more finals follow:
text="", processing_mode="low_confidence_transcript"):
Sent only when the text was too short and too unsure to trust — confidence below 0.55, at most 4 words, at most 14 characters. The event still arrives so you can clear the interim it replaces; it is not billed. Longer or confident text is never withheld, and a continuation after a short-pause split is never withheld.
is_final=false, speech_final=false) — only sent when interim_results_frequency is set:
is_final=true, speech_final=true will follow.
Response Fields:
string
Transcribed text. Empty string = speech-end-no-result signal.
number
0.0–1.0. Telephony typically 0.35–0.75; browser 0.55–0.95.
string
Detected language code e.g.
"en".string
Uppercase language code e.g.
"EN".boolean
false = interim · true = committed final for this stretch of audio.boolean
Same as
is_final. The two flags always agree — there is no separate “canonical” phase.boolean
true = this final was cut by the max_utterance_sec length cap and more of the same turn follows. Check this before speech_final so a segment isn’t treated as the end of the turn.boolean
true for interim results only.integer
Turn number. Interims carry the id their final will have.
sentence_id is the same value.string
Unique id of a committed result. Use it to de-duplicate — if a network retry delivers one twice, drop the duplicate.
string
What ended the turn:
vad_silence, client_finalize, client_stop, max_utterance, language_change, or response_timeout. interim on interims.number
Seconds of audio covered by this final. (
utterance_end_ms is the same value in milliseconds.)number
Server processing time in seconds.
array
Finals only:
[{ word, start, end, probability }], seconds from turn start. The score field is probability. Empty on interims. For Indian languages the times are evenly spread estimates and probability repeats confidence.integer
duration in milliseconds.string
How this result was produced. On ordinary results:
streaming_interim (interim), sentence_complete (final), max_utterance_segment (segment final). On a withheld final: low_confidence_transcript (empty text; see Withheld final above). Treat unknown values as “withheld”.string | null
Only with
diarize: true — dominant speaker label for the final (also speaker_label, speakers).language_changed — After config message changes language
config changes are acknowledged with their own *_changed frame (turn_detector_changed, utterance_end_changed, interim_frequency_changed, audio_enhancement_changed, audio_format_changed, keywords_changed, context_changed, …).
no_speech — The turn had no words
terminal: true— the turn is over with no words. Clear any interim text; if you collected segment finals for this turn, dispatch them now. Not billed.terminal: false— an interim check found no words; the turn is still open. Ignore it (or clear the interim display). There is notextfield.
turn_complete — End-of-thought confirmed (only with turn_detector: true)
Sent right before the final it confirms.
forced: true means the turn was ended by the safety limit (3 × utterance_end_ms) even though the check judged it unfinished.
interim_delta — Delta for an interim (only with interim_delta_streaming: true)
operation: "append" → add text to the line. operation: "replace" → the guess changed; replace the line with full_text.
transcription_tokens — Word tokens (only with token_streaming: true)
Word tokens with stable ids for UIs that animate words. transcription stays the source of truth. event is append for interims, finalize for the final.
warning — Non-fatal notice
encoding/sample_rate declared in start (it’s then treated as 8 kHz μ-law).
session_stopped — After stop is processed
is_final: true, non-empty text) is charged by its duration. Because a turn’s several finals cover non-overlapping audio, billing each one sums to the real spoken time with no double-charge. Withheld/empty finals (low_confidence_transcript) and no_speech cost nothing. client_estimated_seconds is a diagnostic estimate of what the client sent; never show it as a billed figure.
error — Rejected message or failed final
Two kinds, told apart by terminal:
- Non-terminal (no
terminalfield) — the server rejected a client message (invalidstart/config, unknown type, audio-format mismatch). The turn is unaffected; fix the client and resend. - Terminal (
terminal: true) — a turn’s recognition failed (failure_typesuch astranscription_timeout,transcription_busy,backend_unavailable,language_identification_uncertain). That turn is closed without text but the session stays open. Handle it like a terminalno_speech: clear the interim, dispatch any finals already collected, keep the session.
test_response — Reply to test ping
Complete Example
Audio Requirements
Supported Languages
45 languages — 12 Indian (as, bn, gu, hi, kn, ml, mr, ne, or, pa, ta, te) plus 33 others including English, Spanish, French, German, Arabic, Mandarin (zh), Japanese (ja), Korean (ko), Turkish (tr), Vietnamese (vi), and Hebrew (he). Fetch the authoritative catalog (with each language’s quality_tier) from GET /v1/languages (or GET /stt/languages via the proxy).
Send a small explicit languages list — each extra language is another chance of a wrong pick. Mixed speech such as Hinglish is handled within a turn (Hindi in Devanagari, English words in Latin); en + hi is the most-tested pair. Arabic dialect tags (ar-eg, …) are rejected — pass ar for best-effort MSA.
Common languages:
Pricing
- Rate: $0.00000833 per second
- Minimum: $0.01 per session
- Billing: Per second of audio processed
Related
- TTS WebSocket - Text-to-Speech endpoint
- WebSocket Quick Start - Get started guide
- WebSocket Playground - Test in browser