Skip to main content
Stream audio to a model deployed with the Baseten speech-to-text (STT) server and receive partial and final transcripts over a WebSocket. This reference defines the shared v2 protocol; for model-specific protocols, see Real-time transcription API (v1).

Deployment compatibility

The deployed server determines the protocol; the WebSocket URL doesn’t contain a protocol version. The Qwen3-ASR Deploy Streaming option in the Model Library uses v1. It doesn’t send session.created and can’t run a v2 client. To use v2, contact Baseten support for a unified STT server deployment. Confirm that it sends session.created before integrating. For a complete client and recording, follow Transcribe with the unified API.

Connection

This is the production environment’s WebSocket endpoint. To validate a unified deployment before promoting it, use its deployment endpoint:
Unified STT presets use a custom Docker server, which requires a published deployment. Development deployments aren’t supported for these presets. For connection behavior, see WebSockets.

Parameters

string
required
The ID of the model you want to call.
string
The published deployment ID. Required when using the deployment endpoint.
string
required
Your Baseten API key: Authorization: Bearer $BASETEN_API_KEY. The server also accepts Api-Key as the scheme.
Baseten checks your API key during the WebSocket handshake. If the check fails, the handshake fails with an HTTP status and the socket never opens: 400 with no Authorization header, or 403 if the key is invalid or can’t access the model. You don’t get an error event or a close code. See how a request reaches your model. The browser’s WebSocket API can’t set the Authorization header. Connect through your backend, which adds the header and keeps your API key on the server. If the deployment has scaled to zero, Baseten holds the connection until a replica is ready, which can take several minutes. If every replica is at its concurrency target, the connection waits for a free slot or a new replica. Set your client’s handshake timeout to allow for this, or set a minimum replica count greater than zero. See what happens when no replica is available.

Session flow

  1. Connect. The server sends session.created with the model’s capabilities and default settings.
  2. Optionally, send session.update to change the settings. The server replies with session.updated, listing what it granted and anything it refused.
  3. Stream audio with input_audio_buffer.append.
  4. End each turn with input_audio_buffer.flush (client_flush), or let a pause end it (server_vad). See segmentation modes.
  5. Receive transcription.done with each turn’s final transcript. If you enable partials, the server also sends transcription.delta while audio arrives.
  6. After every turn has a final result, send close. The server closes the socket with code 1000. A normal close alone doesn’t confirm that all audio was transcribed.
Configure the session before sending audio. To change settings after audio starts, open a new connection.

Client events

Every event is a JSON object with a type field. The server ends the session with an invalid_event error for any unknown type or field, wrong value type, or out-of-range value, including misspelled field names.

session.update

Configures the session. If you omit this event, the session uses defaults from session.created. The server reports unsupported settings in session.updated.refusals and the effective settings in session.updated.granted. Include every setting you want to keep in each update. Omitted fields reset to their defaults, except context, which retains its previous value.
object
The format of the audio you send. Each model accepts one format, shown in capabilities.input_format. Send audio in that format; resample or downmix it first if needed. You can leave this field out.
string
What ends a turn: client_flush or server_vad. See segmentation modes. Defaults to the first mode in capabilities.segmentation_modes. The server refuses modes the model doesn’t support.
string
The language to transcribe, as a BCP-47 code such as en, or auto to detect it. Defaults to capabilities.default_language.If there’s no exact match, the server drops the region and tries again, so en-US gets en. The server refuses unsupported languages and keeps the default. It also refuses this field if the model doesn’t support language selection. A value longer than 35 characters is an invalid_event error.
float
How often, in seconds, the server sends transcription.delta events during a turn. Partials are off unless you set this.The server raises shorter intervals to capabilities.min_partial_interval_s. A value of 0 or less is an invalid_event error. The server refuses this field if the model doesn’t produce partials.
object
Text that helps the model recognize words from your domain. If you leave it out, the session keeps the context from your last update, not the default. If capabilities.context is absent, the model takes no context and the field is refused. If the model takes only text or only keyterms, the other is refused.If the model sets capabilities.context.max_words, keyterms are dropped from the end of the list until the context fits, and text is dropped if it’s too long by itself. Each drop is listed as a refusal, and granted.context.truncated is true if any keyterms were dropped. If nothing fits, granted.context is absent.
object
Turn detector settings for server_vad sessions. Every field is optional. Omitted fields use the values from defaults.vad in session.created, which can differ by model or deployment. A value out of range is an invalid_event error.

input_audio_buffer.append

Sends audio for the current turn. You can send audio in real time or faster. Under server_vad, use small chunks, such as 100 ms of audio, so the voice detector can observe speech and silence across messages. A large append containing an entire utterance and its trailing silence can miss the turn boundary. Under client_flush, your flush ends the turn, so you can send larger chunks within the WebSocket message size limit.
string
required
Base64-encoded audio in the granted input_format. Invalid base64 is an invalid_event error.

input_audio_buffer.flush

Ends the current turn. Takes no other fields: {"type": "input_audio_buffer.flush"}. The server transcribes the turn’s audio and replies with transcription.done, or transcription.failed if it can’t. Keep receiving until that result arrives; closing the session or losing the connection can interrupt pending work. If you flush with no audio, the transcript is empty. Under server_vad, a flush ends the turn without waiting for a pause.

input_audio_buffer.clear

Discards the current turn’s audio without transcribing it. Takes no other fields: {"type": "input_audio_buffer.clear"}. The server replies with input_audio_buffer.cleared. The discarded turn still uses up a turn_id, and its audio still counts toward the session’s timestamps.

close

Ends the session. Takes no other fields: {"type": "close"}. The server drains pending transcription work for a limited time, then closes the socket with code 1000. The default drain timeout is 5 seconds; deployments can configure it. The server can abandon work that exceeds the timeout even when the close code is 1000. Flush the last turn and wait for every expected transcription.done before sending close. Treat transcription.failed, a receive timeout, or closure before an expected result as incomplete transcription. Use a receive timeout appropriate for your workload. Without a flush, the server transcribes the last turn only if its voice detector heard speech in it, in either segmentation mode. Audio it scored as silence is dropped.

Server events

session.created

The first message on every connection. If the replica is full, you get an at_capacity error instead.
string
The session’s ID. Include it in support requests.
object
The model’s supported settings and limits. Unsupported optional fields are absent.
object
The settings the session uses if you don’t send session.update. Same shape as granted in session.updated.

session.updated

The reply to every session.update.
string
The session’s ID.
object
The effective session settings. Use these values to configure your client.
array
Settings that differ from your request. An empty array means the server granted every requested setting.

transcription.delta

A partial transcript of the turn in progress. Sent only when granted.partials is true, about every partial_interval_s seconds while new audio arrives. Use capabilities.partial_style to decide how to combine them. Some models decode each partial from only the most recent audio in the turn. Qwen3-ASR uses the last 8 seconds, so on a longer turn its partials show only the end of what was said. The final always covers the whole turn. Partials can also arrive after you flush, until the turn’s final arrives.
object

transcription.done

A turn’s final transcript.
object
string
max_turn_length if the server cut the turn at max_turn_s. The speaker didn’t necessarily stop, so don’t treat it as the end of a sentence. Absent on every other final.

transcription.failed

A turn couldn’t be transcribed. The server sends this instead of the turn’s transcription.done. The session remains open for subsequent turns.
integer
The turn that failed.
string
decode_failed: the model couldn’t transcribe this turn’s audio.
string
What went wrong.

input_audio_buffer.cleared

The reply to input_audio_buffer.clear.
integer
The ID of the discarded turn.

error

Describes the error that causes the server to close the socket. See errors and close codes.
string
invalid_event, internal_error, or at_capacity.
string
A description of the error.

Transcript object

transcription.delta and transcription.done carry the same transcript object.
integer
The turn this transcript belongs to, starting at 0. Use it to match partials to their final.
array
The transcribed text, in order. A turn with no speech can have no segments, or segments with empty text. Join the text before deciding a turn is empty, and don’t assume there’s a first segment.
string
The language the text was transcribed in, as a BCP-47 code from capabilities.languages. Absent when it isn’t known, for example a turn with no text, or auto on a model that doesn’t report what it detected. A partial’s language can differ from its final’s; the final’s is the one to keep.
Timings count all the audio sent in the session, including turns discarded with input_audio_buffer.clear. Every segment has start and end, but their precision depends on the model.

Segmentation modes

Each session uses one mode. capabilities.segmentation_modes lists the modes a model supports.

Long turns

In either mode, the server cuts a turn when it reaches capabilities.max_turn_s seconds, so a long stretch of speech becomes several turns. Speech continues in the next turn. A cut turn’s transcription.done has ended_by: "max_turn_length". Silence the server skips doesn’t count toward the limit.

Errors and close codes

When the server closes the socket because of an error, it first sends an error event with the reason. Use the close code to decide what to do: If a connection reaches a replica before its model has loaded, the server closes with 1013 and no error event. Usually Baseten holds the connection until the replica is ready instead; see connection. A rejected API key fails the handshake before the socket opens, so there’s no close code. See connection. A failure that affects only one turn doesn’t close the session. The server sends transcription.failed for that turn instead.

Transcribe an audio file

Follow Transcribe with the unified API for a complete Python client, a sample recording, and completion handling.

Example session

The following session.created example is from a Qwen3-ASR unified deployment. Capabilities, limits, and defaults depend on the deployment; read them from your own connection. The session.updated example answers this request. Qwen3-ASR raises the requested partial interval from 0.2 s to its 0.3 s minimum and reports the change in refusals:
session.update