Realtime Speech to Text
Live transcription over a WebSocket: mint a short-lived token, stream fixed-format PCM, read partials and finals as they stabilise.
Almost none of this endpoint is HTTP: POST /api/v1/speech-to-text/stream/token is the only request, and everything after happens on a WebSocket straight to the transcription engine. A token is good for one streaming session and expires 600 seconds after minting — that is also the longest a single session may run, so long recordings re-mint and reconnect. The audio contract is fixed: raw 16-bit little-endian PCM (pcm_s16le), 16,000 Hz sample rate, one channel, sent as binary WebSocket frames — no WAV header, no other rate, no stereo. The mint response repeats the contract (audio_format, sample_rate, channels) so your client can assert it rather than assume it.
Authentication
The token mint takes your account API key (sv-api-key) with the stt:transcribe scope, on a plan that includes realtime speech-to-text — Creator and up today; without it the mint answers 403 plan_limit_exceeded. The WebSocket itself never sees your API key: connect with the minted token, either as an Authorization: Bearer header or — for browsers, which cannot set WebSocket headers — as ?token= on the URL.
Create a key in Settings → API Keys. It is shown once, so copy it then. Every example below reads it from $SONICVOX_API_KEY.
Endpoints
Mint a streaming session. The optional body { "language": "en" } sets a language hint (also settable over the socket with a config message). The response names the wsUrl to connect to, the token, and its unix-seconds expiresAt. Billing is settled, not flat: one minute of transcription is reserved at mint as an affordability check — sized against your plan's included STT hours, so reserved_credits is 0 when your allowance covers it — and when the stream ends the engine reports the real duration and the session settles to it: unused reserve refunded, extra time charged.
- Scope
- stt:transcribe
- Credits
- 1-minute reserve at mint (0 if plan STT hours cover it); settled to real duration — 300/min beyond included hours
curl https://staging.sonicvox.ai/api/v1/speech-to-text/stream/token \
-X POST \
-H "sv-api-key: $SONICVOX_API_KEY" \
-H "content-type: application/json" \
-d '{"language": "en"}'
# 503 service_unavailable here means realtime is not yet enabled in this
# environment (see the availability note) — nothing was charged.{
"wsUrl": "wss://<engine-host>/stream",
"token": "t:<user>:<expiry>:<nonce>:<hmac>",
"expiresAt": 1793476800,
"language": "en",
"reserved_credits": 300,
"audio_format": "pcm_s16le",
"sample_rate": 16000,
"channels": 1
}
# Then, on the socket —
# client → server (binary): raw pcm_s16le 16 kHz mono frames
# client → server (text): {"type":"config","language":"en"}
# {"type":"flush"} {"type":"close"}
# server → client (text): {"type":"partial","text":"..."}
# {"type":"final","text":"...",
# "start":0.32,"end":1.78,"confidence":0.94}
# {"type":"error","message":"..."}When it fails
Every error carries type, code, message, request_id and a doc_url. Branch on type for retry policy.
| service_unavailable | Realtime capacity is not yet switched on for this environment — the mint fails CLOSED rather than handing out a socket that cannot connect, and nothing is charged. This is the rollout state, not a fault in your request. |
| plan_limit_exceeded | The plan does not include realtime speech-to-text (it starts on Creator), or does not include API access at all. |
| insufficient_credits | The 1-minute reserve could not be taken. No session started and nothing was charged. |
| account_limited | Temporary throttle after repeated content-policy violations. It clears on its own; retrying extends it. |