Streaming

Real-time ASR over WebSocket for live transcription, dictation, and voice agents. The endpoint emits partial transcripts as audio streams in, and a final transcript at end-of-utterance.

Streaming language coverage varies. For the current list of supported locales and access details, email [email protected].

TTS streaming

Synthesis is batch-only today via POST /v1/tts; there is no /v1/tts/stream equivalent yet. For voice agent use cases that need low latency, the Fast quality tier (options.qualityTier: "fast") is the lowest- latency path the batch endpoint offers.

Endpoint

WebSocket
wss://api.phonetico.ai/v1/asr/stream

Protocol

  1. Open the WebSocket.
  2. Send an init JSON message with your API key and localeCode. You have 5 seconds (streamInitTimeoutSeconds in GET /v1/asr/limits) to send it, or the connection closes with code 4001.
  3. Wait for ready. Audio sent before it is held and forwarded in order once the stream opens; if the stream is refused, held audio is discarded and never billed.
  4. Stream raw PCM Int16 binary frames (16 kHz, mono, little-endian).
  5. Receive partial messages as the model produces them. stablePrefixLen tells you how many characters of the partial are stable and won't change.
  6. Send { "type": "end" } when done, or close the socket. Server emits a final final message and closes with 1000.

Frames

TypeDirectionWhenBody
initclient → serverFirst message. Add "retainTranscript": false to keep nothing of this stream{ "type": "init", "apiKey": "pk_live_...", "localeCode": "amh" }
readyserver → clientAuth + upstream open succeed{ "type": "ready" }
queuedserver → clientUpstream is queueing (busy GPU); arrives between init and ready{ "type": "queued" }
audioclient → serverAfter init; frames sent before ready are held and forwarded in orderBinary frames: raw PCM Int16, 16 kHz mono, little-endian
partialserver → clientDuring streaming{ "type": "partial", "text": "...", "stablePrefixLen": 12 }
finalserver → clientOnce, after you send end; transcriptRetainedUntil is null when nothing is kept{ "type": "final", "text": "...", "audioDurationMs": 4200, "confidence": 0.93, "transcriptRetainedUntil": "2026-10-29T14:03:11.000Z" }
errorserver → clientCredit runs out mid-stream (insufficient_balance), or the key reaches its monthly spend cap (key_spend_cap): later audio is dropped, the audio before it is transcribed and billed, then the stream closes 1000{ "type": "error", "code": "insufficient_balance", "message": "Stream ended at 60.0 s: ..." }
endclient → serverNo more audio coming{ "type": "end" }
cancelclient → serverAbort the stream{ "type": "cancel" }

Example

import WebSocket from "ws";
import { readFileSync } from "node:fs";

const ws = new WebSocket("wss://api.phonetico.ai/v1/asr/stream");

ws.on("open", () => {
  ws.send(JSON.stringify({
    type: "init",
    apiKey: "pk_live_your_key_here",
    localeCode: "en-US",
  }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());

  if (msg.type === "ready") {
    // Stream raw PCM Int16 16kHz mono in 320-sample (20ms) chunks
    const pcm = readFileSync("speech.pcm");
    const chunkSize = 640; // 320 samples * 2 bytes
    for (let i = 0; i < pcm.length; i += chunkSize) {
      ws.send(pcm.subarray(i, i + chunkSize));
    }
    ws.send(JSON.stringify({ type: "end" }));
  } else if (msg.type === "partial") {
    process.stdout.write("\r" + msg.text);
  } else if (msg.type === "final") {
    console.log("\nFinal:", msg.text);
  }
});

Close codes

CodeMeaning
1000Normal close. Stream completed.
4001Auth error. Missing/invalid init message or API key.
4002Not enough credit to open. A stream opens holding the cost of its first stretch of audio; an error frame with code insufficient_balance, naming the amount needed and available, arrives first, or key_spend_cap when the hold would take the key past its monthly spend cap. Device keys: monthly minutes used.
4003Audio too long. Exceeded the per-stream duration cap.
4004Transcription for this language is temporarily unavailable. When found at init, an error frame with code capability_unavailable arrives first.
4005Upstream ASR stream error during transcription.
4029Request limit reached for this key. The close reason names the window.
4400Language or streaming not offered. An error frame with code capability_unavailable, naming both, arrives first.
4403The key is not allowed here: not allowed to use transcription (its product scopes; error frame code key_scope) or not from one of its allowed networks (code key_network_not_allowed), or the account has not accepted the current developer terms (code terms_not_accepted). The error frame arrives first.

Tips

  • Send 20-40 ms frames (320-640 samples). Smaller frames waste overhead, larger ones add latency.
  • stablePrefixLen on partial messages lets you commit a prefix to the UI without flicker. The suffix may still change.
  • A stream carries up to 300 seconds of audio (maxAudioDurationSeconds). For a longer recording, use a transcription job.
  • For browsers, capture with AudioWorkletNode, downsample to 16 kHz, and pack to Int16. The MediaRecorder WebM/Opus output is not accepted on this endpoint.