Text-to-SpeechReal-time API

Connection keepalive

Learn how keepalive keeps a WebSocket connection open between streams in Soniox Text-to-Speech, and why it does not keep an individual stream open.

Overview

Keepalive keeps an idle WebSocket connection open between streams, so you can start the next stream on the same socket without reconnecting. This is useful in a conversational agent, where there are gaps between turns while the user speaks or an upstream LLM is thinking.

To keep the connection open, send a keepalive control message:

{"keep_alive": true}

Keepalive is a connection-level signal. It does not trigger speech generation and has no effect on any stream.

Keepalive does not keep a stream open. Once a stream has started, the server expects text for it to keep arriving. If more than a few seconds pass without a text message for the stream and you have not sent text_end: true, the stream is terminated with a request_timeout error, no matter how many keepalives you send.


Connection timers vs stream timers

There are two separate timers, and keepalive only affects the connection.

ScopeTimer resets onWhen it fires
StreamA text message for that stream_idA few seconds without text and no text_end: true: the stream ends with request_timeout.
ConnectionAny client message, including keep_aliveAbout 40 seconds with no active stream and no message: the connection is closed as idle.

While a stream is active, the connection is never closed as idle. The connection timer only matters in the gaps between streams.


Send text continuously within a stream

A stream is a single, continuous piece of text, for example one agent reply. Send the first text chunk promptly after the config message, keep sending chunks without pausing for more than a few seconds, and send text_end: true as soon as the text is complete. The server then generates the remaining audio and terminates the stream cleanly. See Stream termination.

If you do not know when the next piece of text will arrive, do not hold the stream open. Send text_end: true, keep the connection open with keepalives, and start a new stream with a new stream_id when the next text is ready.

For example, this sequence fails: the client sends text without text_end: true, then only keepalives while waiting for more text.

{"text": "The sun rises in the east every morning.", "text_end": false, "stream_id": "turn-1"}
{"keep_alive": true}

Audio for the text is delivered, but a few seconds after the last text message the server sends a request_timeout error followed by terminated: true for turn-1.


When to use keepalive

Send a keepalive message when no stream is active and you are waiting for the next text, for example between agent turns or while an LLM generates the next reply. You can also send keepalives on a fixed interval while a stream is active. They are harmless, but they do not extend the stream's text window.


Key points

  • Keepalive keeps the connection open between streams. It does not keep a stream open.
  • Send a keepalive at least once every 20–30 seconds when no stream is active.
  • Keepalive applies to the whole WebSocket connection, not to a specific stream_id.
  • Keepalive works only after you start your first stream. A freshly opened connection must send a config message with a valid API key within about 10 seconds, or it is closed. Keepalive messages do not authenticate the connection.

Keepalive does not keep an unused connection open forever

Keepalive prevents idle timeouts, but a connection that generates no audio for more than 3 minutes is closed even if keepalives are still flowing. Keepalives keep the socket from looking idle, they do not count as work.

To keep a connection open across long gaps, generate audio at least once within every 3-minute window. If your application may go longer than that without speaking, let the connection close and open a new one when you have text to send.