Realtime
Real-time speech-to-text transcription over WebSocket. See the Realtime guide for concepts.
wss://api.reson8.dev/v1/speech-to-text/realtimeRequest
Section titled “Request”Headers
Section titled “Headers”| Header | Value |
|---|---|
| Authorization | ApiKey <api_key> or Bearer <access_token> |
| Sec-WebSocket-Protocol | bearer, <access_token> |
See Authentication for which header to use in different situations.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
encoding |
string | auto |
Audio encoding: auto for detected container formats, pcm_s16le for raw PCM, or mulaw / alaw for raw G.711 telephony audio. See Audio Formats |
sample_rate |
number | 16000 |
Sample rate in Hz (only used depending on encoding) |
channels |
number | 1 |
Number of audio channels, 1-10 (only used depending on encoding) |
language |
string | Language to transcribe. Recommended for best quality. A comma-separated list (e.g. nl,en) constrains per-utterance auto-detection to those candidates. When omitted, the server auto-detects each utterance independently. See Languages for supported codes |
|
phrases |
string | Comma-separated phrases to bias transcription toward, up to 250. See Custom Models | |
custom_model_id |
string | ID of a custom model to bias transcription. Overrides the model configured on the API client | |
bias_strength |
number | 0.45 |
Strength of contextual biasing. Must be a non-negative number |
include_timestamps |
boolean | false |
Include start_ms and duration_ms on transcripts and words |
include_words |
boolean | false |
Include word-level detail on transcripts |
include_language |
boolean | false |
Include the detected language on transcripts (empty on interim) |
include_confidence |
boolean | false |
Include confidence on words |
include_interim |
boolean | false |
Include interim (partial) results and the is_final flag |
diarize |
boolean | false |
Enable speaker diarization. Adds speaker_id to each transcript |
max_speakers |
number | Maximum number of distinct speakers (1-4). When omitted, the count is determined automatically. Only used when diarize=true |
|
patterns |
string | Comma-separated regex-style patterns for short alphanumeric tokens (order codes, licence plates) to recover. Only set when the token is likely present; cannot be combined with phrases or a custom model - see Patterns |
|
filler_mode |
string | natural |
Controls filler words in final transcripts: clean removes them, natural lets the model decide, and verbatim preserves them. Interim transcripts are unaffected |
Example
Section titled “Example”import asyncioimport jsonimport websockets
async def transcribe(): url = "wss://api.reson8.dev/v1/speech-to-text/realtime" headers = {"Authorization": "ApiKey <your_api_key>"}
async with websockets.connect(url, additional_headers=headers) as ws: async def send_audio(): try: with open("recording.wav", "rb") as f: while chunk := f.read(8192): await ws.send(chunk) await ws.send(json.dumps({"type": "flush_request"})) except Exception: await ws.close() raise
sender = asyncio.create_task(send_audio())
async for message in ws: event = json.loads(message) print(event) if event["type"] == "flush_confirmation": break
await sender
asyncio.run(transcribe())const token = "<your_access_token>";const url = "wss://api.reson8.dev/v1/speech-to-text/realtime";
// Passes token via Sec-WebSocket-Protocol headerconst ws = new WebSocket(url, ["bearer", token]);
ws.onopen = async () => { const audio = await (await fetch("/recording.wav")).arrayBuffer(); for (let i = 0; i < audio.byteLength; i += 8192) { ws.send(audio.slice(i, i + 8192)); } ws.send(JSON.stringify({ type: "flush_request" }));};
ws.onmessage = (event) => { const message = JSON.parse(event.data); console.log(message); if (message.type === "flush_confirmation") ws.close();};Sending Messages
Section titled “Sending Messages”Binary WebSocket frame containing audio data.
Flush Request
Section titled “Flush Request”Force the server to finalize any buffered audio and return a transcript.
{ "type": "flush_request", "id": "abc123"}| Field | Type | Required | Description |
|---|---|---|---|
id |
string | No | Optional identifier, returned in the corresponding flush confirmation |
Receiving Messages
Section titled “Receiving Messages”Transcript
Section titled “Transcript”Returned when speech is recognized. By default, only final results are returned. When include_interim=true, interim (partial) results are also returned - these may change as more audio is processed.
{ "type": "transcript", "text": "the patient presented with chest pain"}With diarize=true, each transcript carries the speaker_id of its dominant speaker.
{ "type": "transcript", "text": "where does it hurt", "speaker_id": 0 }{ "type": "transcript", "text": "my chest, mostly", "speaker_id": 1 }{ "type": "transcript", "text": "the patient presented with chest pain", "language": "en", "is_final": true, "start_ms": 1200, "duration_ms": 2400, "speaker_id": 0, "words": [ { "text": "the", "start_ms": 1200, "duration_ms": 200, "confidence": 0.990 }, { "text": "patient", "start_ms": 1410, "duration_ms": 450, "confidence": 0.980 }, { "text": "presented", "start_ms": 1880, "duration_ms": 500, "confidence": 0.970 }, { "text": "with", "start_ms": 2400, "duration_ms": 200, "confidence": 0.990 }, { "text": "chest", "start_ms": 2620, "duration_ms": 350, "confidence": 0.960 }, { "text": "pain", "start_ms": 3000, "duration_ms": 600, "confidence": 0.970 } ]}| Field | Type | Included | Description |
|---|---|---|---|
text |
string | Always | The recognized text |
language |
string | When include_language=true |
The detected language code (empty on interim) |
is_final |
boolean | When include_interim=true |
true for final, false for interim |
start_ms |
number | When include_timestamps=true |
Start time in milliseconds |
duration_ms |
number | When include_timestamps=true |
Duration in milliseconds |
speaker_id |
number | When diarize=true |
Speaker label for this transcript (integer, 0-indexed). See Diarization |
words |
array | When include_words=true |
Word-level detail |
Each word contains:
| Field | Type | Included | Description |
|---|---|---|---|
text |
string | Always | The recognized word |
start_ms |
number | When include_timestamps=true |
Start time in milliseconds |
duration_ms |
number | When include_timestamps=true |
Duration in milliseconds |
confidence |
number | When include_confidence=true |
Probability in (0, 1] |
Flush Confirmation
Section titled “Flush Confirmation”Sent after a flush request has been processed. The id field is always present; it is null if no identifier was provided in the request.
{ "type": "flush_confirmation", "id": "abc123"}| Field | Type | Included | Description |
|---|---|---|---|
id |
string | Always | The identifier from the corresponding flush request, or null if none was provided |
Errors
Section titled “Errors”Rejected WebSocket upgrades return no body; where available, the reason is in the X-Error-Message response header.
| Status | Description |
|---|---|
| 400 Bad Request | Invalid query parameter, unknown custom_model_id, or patterns combined with a custom model |
| 401 Unauthorized | Missing or invalid credentials |
| 402 Payment Required | Credit limit exceeded - see Limits |
| 429 Too Many Requests | Concurrent connection limit exceeded - see Limits |
Invalid Client Messages
Section titled “Invalid Client Messages”Malformed or unrecognized client messages are ignored; the session continues.
Close Codes
Section titled “Close Codes”After the connection is established, errors are signalled through the WebSocket close code and reason (in browsers: event.code and event.reason on the close event). The reason is a stable machine-readable token: branch on the close code for coarse handling and on the reason for specifics.
| Close code | Reason | Description |
|---|---|---|
| 1000 | Normal closure | |
| 1011 | internal_error |
Unexpected server failure. Reconnect and resend unconfirmed audio |
| 1011 | internal_timeout |
An internal service did not recover within its reconnect budget (up to ~5 seconds). Reconnect and resend unconfirmed audio; back off if it recurs |
| 4000 | message_backlog |
Client did not read messages for 10 seconds. Consume promptly, then reconnect |
A connection that drops without a close frame (surfaced as code 1006 in browsers) indicates a network failure or server crash. Treat it like 1011: reconnect and resend unconfirmed audio.