Skip to main content

Overview

Send audio as binary WebSocket frames after session_ready. The format you stream must match what you declared in the audio object of your config. Frames may be any length — the server re-frames the stream internally.
Recommended: raw 16-bit PCM (pcm_s16le), 16 kHz, mono. This is the lowest-overhead, best-supported format.

Format

Encoding

  • pcm_s16le — raw 16-bit little-endian PCM (preferred)
  • wav — WAV container

Stream shape

  • Binary frames, any length
  • Server re-frames internally
  • No client-side segment alignment needed

Sample rate

audio.sample_rate
integer
default:"16000"
Accepted range 8000–48000 Hz. 16000 is recommended and is what most integrations should send.

Channels

audio.channels
integer
default:"1"
1 (mono, recommended) or 2. Mono keeps bandwidth and processing minimal; use it unless you have a specific reason not to.
SettingRecommendedAccepted
Encodingpcm_s16lepcm_s16le, wav
Sample rate16000 Hz800048000 Hz
Channels1 (mono)1 or 2
Bit depth16-bit16-bit
For pcm_s16le, send interleaved signed 16-bit samples with no WAV header. For wav, the container header is read from the stream.

Chunking guidance

You do not chunk on segment boundaries — the server decides where segments end. Two independent things are worth getting right:
  1. How often you send binary frames (transport pacing).
  2. Which chunk.strategy you choose (how the server finalizes segments).

Transport pacing

Send audio at roughly real time, in small frames. Frames of about 20–100 ms of audio (for example ~640–3200 bytes at 16 kHz mono pcm_s16le) keep captions responsive without flooding the socket.
Do not dump a long recording as fast as possible. Sending far ahead of real time can trigger a backpressure signal and, if sustained, dropped segments. Pace your sends to the audio’s own duration.

Choosing a strategy

The chunk.strategy trades latency against accuracy and text density. Pick by behavior, not by frame size:

low_latency

Shortest segments, fastest captions. Best for live on-screen dictation.

balanced

The default. Strong accuracy while still feeling live.

high_throughput

Longest segments, highest accuracy and density. Best when a little extra delay is acceptable.
Silence trimming and pause-based segmentation are automatic. You can send continuous audio (including silence) and let the server decide where each segment ends.

Browser capture example

// Capture mic audio at 16 kHz mono and stream it as pcm_s16le binary frames.
async function startMicrophone(ws) {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: {
      sampleRate: 16000,
      channelCount: 1,
      echoCancellation: true,
      noiseSuppression: true,
    },
  });
  const ctx = new AudioContext({ sampleRate: 16000 });
  const source = ctx.createMediaStreamSource(stream);
  const node = ctx.createScriptProcessor(2048, 1, 1); // ~128 ms per callback

  node.onaudioprocess = (e) => {
    const f32 = e.inputBuffer.getChannelData(0);
    const pcm = new Int16Array(f32.length);
    for (let i = 0; i < f32.length; i++) {
      pcm[i] = Math.max(-1, Math.min(1, f32[i])) * 0x7fff; // float32 -> int16
    }
    if (ws.readyState === WebSocket.OPEN) ws.send(pcm.buffer);
  };

  source.connect(node);
  node.connect(ctx.destination);
}
import asyncio
import numpy as np
import sounddevice as sd

async def stream_microphone(ws, sample_rate=16000, block_ms=40):
    """Capture 16 kHz mono audio and send it as pcm_s16le binary frames."""
    block = int(sample_rate * block_ms / 1000)
    loop = asyncio.get_running_loop()
    queue: asyncio.Queue = asyncio.Queue()

    def callback(indata, frames, time_info, status):
        pcm = (np.clip(indata[:, 0], -1.0, 1.0) * 32767).astype("<i2").tobytes()
        loop.call_soon_threadsafe(queue.put_nowait, pcm)

    with sd.InputStream(samplerate=sample_rate, channels=1,
                        dtype="float32", blocksize=block, callback=callback):
        while True:
            await ws.send(await queue.get())   # binary frame, ~40 ms of audio

Streaming a file

To transcribe a pre-recorded file, decode it to 16 kHz mono pcm_s16le and send it paced to real time (or slightly faster), watching for backpressure. For whole-file, non-real-time transcription, use the batch transcription flow instead of this live socket.
  • Confirm you waited for session_ready before sending audio.
  • Confirm the bytes match your declared encoding, sample_rate, and channels.
  • Confirm you are sending binary frames (not base64 or JSON).
  • Very short bursts of audio may not finalize until more speech (or an end_utterance) arrives.
  • Switch chunk.strategy to low_latency for faster segments.
  • Send smaller frames closer to real time.
  • Check for backpressure frames — if present, you are sending too fast.
  • Provide a language hint in your config to bias detection.
  • Detection is automatic and per-segment; mid-conversation switches are expected and handled.

Next steps

Messages

Send audio and control frames; read transcripts.

Session config

Declare your audio format and chunk strategy.