Streams
Run multiple concurrent Text-to-Speech streams over a single WebSocket connection using stream_id.
Overview
A single WebSocket connection can host up to 5 concurrent streams, each an independent Text-to-Speech generation identified by a unique stream_id. This lets you serve multiple users, voices, or languages from one connection without reopening a socket per request.
Multiple streams are useful when you need to:
- Generate speech for multiple users or conversations over one connection.
- Produce audio in different voices or languages simultaneously.
- Reduce connection overhead by reusing a single WebSocket instead of opening one per request.
Each stream has its own lifecycle: it is started with a config message, receives its own text chunks, and produces its own audio output. Streams are fully isolated — an error in one stream does not affect the others.
Core rules
- Start each stream with a config message that includes a unique
stream_id. See the WebSocket API reference for the full payload. - Use the same
stream_idfor all text chunks belonging to that stream. - You can run up to 5 active streams per connection.
- A stream error does not terminate other streams on the same connection.
- Keep the WebSocket connection alive with keepalive messages between streams. Keepalive does not keep a stream open: send text continuously and close each stream with
text_end: true. - A stream can be canceled at any time.
Starting multiple streams
Send a config message for each stream before sending text for that stream. Stream configs are independent — each one picks its own voice, language, and audio output settings.
Example: two streams on one connection
Stream A config (English, voice Adrian):
Stream B config (Italian, voice Adrian) — same connection, different stream_id, any other fields may differ too:
Important: Always send a stream's config message before sending any text for that stream.
After configuring, send text chunks with the matching stream_id. You can interleave chunks across streams freely — only the stream_id field determines routing:
Route messages by stream ID
The server sends audio for all active streams over the same connection. Use the stream_id field to route each incoming message to the correct output:
- Inspect
stream_idon every incoming message. - Route
audiochunks to the matching output buffer or player. - Track termination state separately for each stream.
- Mark a stream as finished only after receiving
terminated: truefor thatstream_id.
Audio message:
Termination message:
Error isolation
- Stream-level error — The server returns an error message for that
stream_id, followed by aterminated: trueevent. Other streams on the connection continue normally. - Validation or malformed-message error — An incoming message was malformed or invalid (bad JSON, missing fields, unknown
stream_id, duplicate start). The server returns an error response but does not send aterminatedevent, because no stream was actually running. The connection stays open for other valid streams and messages. - Connection-level failure — If the WebSocket itself closes (network failure, server error), all active streams on that connection end immediately.
For the full list of error codes and messages, see the WebSocket API reference.
Code example
Prerequisite: Complete the steps in Get started.
See on GitHub: soniox_sdk_realtime.py.