Skip to main content
WSS
Low-latency English speech-to-text over WebSocket. Emits a rolling partial transcript every ~1.5 seconds while audio streams in, then delivers one complete final transcript at end-of-stream — or, with endpointing enabled, a final transcript for each utterance as the speaker pauses. With diarize=true, each final utterance also carries a speaker label. No emotion detection, accent detection, or PII/PHI tagging.

Endpoint

Authentication

Pass your API key as a query parameter when opening the connection.
Unlike the batch endpoints, this API does not use an X-API-Key header. The key must be in the query string at connection time.
See Authentication and rate limits for how to obtain and manage API keys.

Supported audio formats

Container formats — sample_rate and num_channels are ignored if supplied: mp3, wav, flac, m4a, mp4, ogg, opus, webm, aac, aiff, wma, amr, and au, plus 66 others
3g2, 3ga, 3gp, 3gpp, 8svx, aa3, aac, ac3, act, adts, aif, aifc, aiff, amb, amr, asf, at3, au, avr, awb, bwf, c2, caf, dss, dts, dtshd, eac3, ec3, f4a, f4b, flac, gsm, iff, m2a, m2ts, m4a, m4b, m4r, m4v, mka, mkv, mlp, mp+, mp1, mp2, mp3, mp4, mpa, mpc, mpga, mpp, mts, oga, ogg, ogx, oma, omg, opus, paf, pvf, qcp, ra, rf64, rm, rmvb, snd, svx, thd, ts, tta, voc, vqf, w64, wav, wave, weba, webm, wma, wmv
The MP4-family values (mp4, m4a, m4b, m4r, m4v, 3gp, 3gpp, 3ga, 3g2, f4a, f4b) must be sent in a streamable layout; otherwise the connection ends with an audio-processing error. Opus audio is accepted either as opus or, for Opus-in-Ogg streams, as ogg. Raw PCM formats — sample_rate and num_channels are required: s8, s16le, s16be, s24le, s24be, s32le, s32be, u8, u16le, u16be, u24le, u24be, u32le, u32be, f32le, f32be, f64le, f64be, mulaw, alaw, g722, vox g722 and vox are mono-only: num_channels must be 1. Valid sample rates: 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 For lowest end-to-end latency, send audio_format=s16le&sample_rate=16000&num_channels=1. This matches the model’s native input format and bypasses the server’s audio decoder entirely.

Query parameters

diarize and endpointing are independent; all four combinations are valid. This endpoint accepts no other enrichment toggles. Emotion, accent, and PII parameters are not recognized and have no effect. Use STT Streaming if you need those.

Utterance segmentation (endpointing)

By default, the connection produces one final utterance at end-of-stream covering everything you sent, and each partial_utterance reflects the whole stream so far. With endpointing=true, speech is segmented at pauses:
  • Each partial_utterance contains the complete transcript of the current segment so far (still replace, never append).
  • Each time a pause ends a segment, you immediately receive a final utterance for it — final text arrives shortly after the speaker stops, instead of at end-of-stream. Each final carries start_ms and duration_ms for its speech.
  • The full transcript is the concatenation of the final utterance texts, in order.
  • Every connection still ends with at least one final utterance (a stream containing no speech yields one with empty text), followed by done.
Use endpointing for live conversations — voice agents, assistants, meeting captions — where you want final text per utterance as it happens. Leave it off for whole-recording transcription where one final transcript is simpler to consume.

Speaker labels

Set diarize=true to label which speaker produced each utterance. Labels are attached to final utterance messages only, never to partial_utterance. speaker is a zero-based index, stable for the lifetime of one connection and meaningless outside it. The same person on a new connection may receive a different index, so the value identifies a speaker within a conversation and is not an identity that can be matched across connections or sessions. A label is assigned when an utterance is finalized and is never revised. There is no back-patching, so an early utterance keeps whatever label it was given. speaker is null when diarization was requested but no speaker could be determined for that utterance, which happens on very short or silent segments and on a brand-new voice that has not yet accumulated enough evidence. That is distinct from the field being absent, which means diarize=true was not requested.

Speaker capacity

Up to ten distinct speakers are labeled per connection. Past that, further speakers are merged into existing labels rather than reported separately: their speech is still transcribed, but speaker is wrong for some utterances.
Worth knowing: speakers_capped is the only signal that this happened. Nothing in the transcript or in speaker itself indicates a merged speaker, so a client acting on speaker identity should check speakers_capped alongside n_speakers.

Utterances that span a speaker change

Segments are cut at pauses regardless of diarize. An utterance that spans a fast exchange with no pause between turns carries the speaker who produced most of it, and speaker_purity reports what proportion that was. 1.0 means the utterance was cleanly single-speaker; a lower value means the single label describes only the majority of it.

With endpointing off

diarize=true&endpointing=false produces one final utterance for the whole stream, so a single speaker cannot describe it. Read n_speakers in that mode and treat speaker as indicative only.

Connection flow

  1. Connect to the WebSocket endpoint with api_key, audio_format, (for raw PCM) sample_rate and num_channels, and optionally endpointing and diarize.
  2. Stream audio as binary WebSocket frames. Frames can be any size; 4–64 KB is typical.
  3. Receive partial_utterance JSON messages every ~1.5 seconds. Each contains the complete transcript of its scope so far — replace any previously displayed partial, do not append.
  4. With endpointing=true, receive a final utterance message each time a pause ends a segment.
  5. Send an empty text frame ("") to signal end of audio.
  6. Receive the final utterance for any content not already finalized at a pause (the whole stream with endpointing off; skipped when the last segment was already finalized mid-stream).
  7. Receive a done message with total audio duration.
  8. The connection closes automatically.

Server messages

partial_utterance

Sent roughly every 1.5 seconds while audio is streaming. Each message contains the complete transcript built so far for the current scope — the whole connection by default, or the current speech segment when endpointing=true. It is not a delta from the previous message: replace your displayed partial text with each new value — never append.
Partials never carry speaker labels, even with diarize=true. Speaker fields appear on final utterance messages only.

utterance

The final transcript for one utterance. Will not be revised. With endpointing off, sent exactly once at end-of-stream, covering the entire audio stream. With endpointing=true, sent each time a pause ends a segment; the full transcript is the concatenation of every utterance text in order.
With diarize=true, the same message carries four additional fields:
With diarize=false no speaker field appears in any message, and the payloads are byte-identical to a non-diarized connection.

done

Sent immediately after the final utterance. Signals stream completion. The connection closes shortly after.

error

Sent if something goes wrong. The connection closes after this message. No further messages follow an error.
Diarization adds two error texts, both closing with 1003:
The second means speaker labelling is not enabled on the serving instance. Retrying does not clear it; reconnect without diarize.

WebSocket close codes

Rate limits

  • Concurrent connection limits apply per model; exceeding them rejects the handshake with close code 4030.
See Authentication and rate limits for retry guidance.

Examples

The examples above work unchanged with endpointing and diarization: append &endpointing=true to the URL and the same message loop prints one [final] line per pause instead of a single one at the end; add &diarize=true as well and each of those lines is prefixed with its speaker label.
WebSocket APIs cannot be tested with cURL. For command-line testing, use websocat.
query
type:object
Partial Utterance Result
type:object

In-progress transcript snapshot for the CURRENT speech segment. Sent zero or more times per segment, roughly every 1.5 seconds. The text field contains the current segment's transcript so far - not a delta, and not the whole stream - with auto-capitalization and auto-punctuation already applied. Clients should REPLACE the current segment's previously displayed partial text, not append. Partials never carry speaker labels, even with diarize=true.

Final Utterance Result
type:object

The final transcript for one segment of speech, with auto-capitalization and auto-punctuation applied, plus the segment's start time and duration. With diarize=true it also carries the speaker who produced it. Sent once per segment (when the segment ends at a pause, and once more for the last segment after end-of-stream), and supersedes the partial_utterance messages for that segment. The full transcript is the concatenation of these texts in order.

Error
type:object

An error occurred during transcription. Sent at most once per connection; the WebSocket closes shortly after. No further messages follow an error.

Transcription Complete
type:object

Transcription completed successfully. Sent exactly once, immediately after the final utterance message. After this message the server closes the WebSocket.

Audio Data Chunk
type:string

Raw audio data chunk for transcription. Sent zero or more times by the client during the stream lifetime. Each chunk's bytes are decoded by the server according to the connection's audio_format query parameter.

End of Audio Stream
type:string

Empty text frame signaling end of audio. Sent exactly once by the client after the last audio chunk. The server responds with a final utterance message followed by a done message, then closes the connection.