WebSocket authentication
How to send your API key with a WebSocket connection, and the timeouts and connection limit that apply.
Overview
Send your API key when you open the connection, in the Authorization header or in the protocols list. The connection is yours from the moment it opens, so you can open it early and start streaming the instant you have audio or text.
This works on the Speech-to-Text and Text-to-Speech WebSocket APIs. Use a regular key from the Soniox Console on your servers and a temporary API key in client apps.
Still sending api_key in the start message? That works until 15 January 2027;
see Migrate WebSocket authentication.
Send the API key with the connection
Authorization header
The standard HTTP header, sent on the WebSocket upgrade request. Most
server-side WebSocket libraries can set it, for example websockets in Python
and ws in Node.js.
Protocols list
Browsers cannot set headers on a WebSocket. Instead, pass the key as the
second argument of new WebSocket(url, protocols): the entry soniox-api-key
followed by the key. The browser sends this list in the
Sec-WebSocket-Protocol header.
A bad key does not fail the handshake: onopen fires, then an error frame
with error_code and error_type arrives in onmessage, and the server
closes the connection.
Text-to-Speech
The Authorization header and the protocols list work the same way on
Text-to-Speech. Leave api_key out of the config message.
Never embed a long-lived API key in a browser or mobile app. Have your backend mint a temporary API key and pass that to the client. The direct stream guide shows the full flow.
Rules
- One key per connection. The key sent with the connection is the connection's key for its whole life. Every stream on that connection runs on it.
- Leave
api_keyout of the start message. The same key is accepted, a different one is refused. See Migrate WebSocket authentication. - Use the Authorization header or the protocols list, not both. Sending
both,
soniox-api-keywithout a key, or a malformedAuthorizationvalue gets a400error frame and the connection closes. - Authentication errors arrive as an error frame after the connection opens, not as a failed handshake, so browsers can read them.
- Temporary keys. Temporary keys start with
snx_temp_and work in the Authorization header and in the protocols list. A key in the oldertemp:format is not a valid protocols list entry; send it in the Authorization header. - Single-use temporary keys. A single-use key sent with the connection allows exactly one stream on that connection. On Text-to-Speech, where one connection can carry several streams, use a regular temporary key at connect if you need more than one stream.
Connection timeouts
Sending the key with the connection lets you open the connection before you have anything to send, and start the instant you do. The connection has no deadline for its first stream: it stays open as long as you keep it alive, and it counts against your connection limit the whole time.
Speech-to-Text
Send a keepalive, {"type": "keepalive"}, at least every 40 seconds until
the start request, or the connection is closed as idle with WebSocket status
1001. Once the stream has started, the
keepalive rules apply as before.
Text-to-Speech
Send any message, for example {"keep_alive": true}, at least every 40
seconds, or the connection is closed as idle with WebSocket status 1001.
After the first stream the same rule applies between streams: a message at
least every 40 seconds, see keepalive.
Opening a connection early
A connection you hold open costs you a connection slot, so open only what you will use. The limit is 40 seconds without a message; send a keepalive every 20 to 30 seconds to stay clear of it. The same rule applies on both products and works from any client, browsers included.
Connection limit
Every open WebSocket connection counts against a connection limit, separate from your concurrency limit. It stops idle connections piling up.
- The limit is a multiple of your concurrent requests limit (Speech-to-Text) or concurrent streams limit (Text-to-Speech). It applies per organization, and per project where a project limit is set. Raising your concurrency limit in the Soniox Console raises the connection limit with it.
- A connection is counted about one second after it opens, or from its start message if that comes first. It is released when it closes.
- A connection slot is not a stream slot. You can hold more connections than streams, but starting a stream still needs a free stream slot.
- Over the limit, the next connection gets a
429max_concurrent_connections_reachederror frame and is closed. Close connections you are not using, or start streams on the ones you hold.
Speech-to-Text connections carry one stream and close with it, so the connection limit only matters there for connections opened ahead of time. Text-to-Speech connections stay open between streams, so count the connections you keep, not the streams you run.
Errors
| Error | error_type | When |
|---|---|---|
400 | invalid_request | Both the Authorization header and the protocols list sent, soniox-api-key in the protocols list without the key, a malformed Authorization value, or a key over the length limit. |
401 | unauthenticated | No key was sent, or the key is invalid or expired. It is checked about one second after connecting, or at the start message if that comes first, and the connection is closed. |
429 | max_concurrent_connections_reached | The organization or project is at its connection limit. |
All other errors, for example a missing permission or an exhausted balance, arrive as an error frame in response to the start message.