Keep-Alive
Keep-Alive
A Pulse STT WebSocket is closed by the server after a period with no inbound frames. In a live conversation, audio arrives continuously and you never hit that limit. In a paused one - the caller is on hold, your agent is waiting on a slow tool call, the user stepped away - you need a way to say “still here” without sending audio.
That is what the keep-alive control frame is for. It is a JSON text frame, it resets the inactivity timer, and it carries no audio.
Sending a keep-alive
Send a JSON text frame with a type of ping:
The server replies on the same socket with:
Three aliases behave identically to ping, so you can reuse whatever your client already emits: keepalive, keep_alive, keep-alive.
The keep-alive is an application-level frame - a normal JSON text message you send like finalize or close_stream. It is not the WebSocket protocol-level ping/pong opcode, which most client libraries handle invisibly and which does not reset the inactivity timer.
What it does and does not do
Because the frame never reaches the model, a keep-alive is not a substitute for audio. It holds the connection open; it does not hold the model session open indefinitely. See Limits.
Example
A 30-second interval is a good default: comfortably inside every limit below, and cheap enough to run unconditionally rather than tracking whether audio is currently flowing.
Limits
Three separate limits apply to a streaming session. Only the first is affected by keep-alives.
On inactivity timeout the server sends {"status":"error","message":"Connection timed out after N seconds of inactivity"} and terminates the socket.
Raising the inactivity timeout
Add timeout=<seconds> to the connection URL to raise the inactivity limit, up to a maximum of 3 hours (10800 seconds):
Values above the maximum are clamped rather than rejected. Keep-alives and timeout solve the same problem from two directions - prefer keep-alives, because they also give you a pong you can use to detect a half-open connection, which a raised timeout does not.
Raising timeout does not extend the 20-minute model session idle window. If your session goes longer than 20 minutes with no audio, the model session is released and the next audio frame will not transcribe. For gaps that long, close the session with {"type":"close_stream"} and open a fresh one when audio resumes.
Cost
Pulse STT is billed on audio duration - the amount of audio you actually send - not on how long the WebSocket stays open. A connection held open with keep-alives and no audio accrues no charge: there is no connection fee and no minimum session duration.
This means suppressing silence is a real lever. If your client already runs voice activity detection, you can stop sending frames during long pauses, hold the socket with keep-alives, and resume when speech starts. Two things to watch when you do:
- Send a little pre-roll. Resume streaming ~200 ms before detected speech onset so the first word is not clipped.
- Finalize explicitly. Pulse’s automatic finalization is driven by trailing silence in the audio. If you stop sending audio, that trailing silence never arrives - send
{"type":"finalize"}to flush the pending transcript.
Related
- Finalize Control - the
finalizeandclose_streamcontrol messages. - Endpointing - trailing-silence finalization, and why it stops when audio stops.
- Real-time Troubleshooting - connection drops and timeouts.
- Pulse STT WebSocket reference - full parameter and control-message reference.

