Kitta AI Docs
API ReferenceSpeech to Text

Realtime ASR

Stream low-latency speech-to-text over WebSocket.

Realtime ASR

Realtime ASR is available through the Open API for live captions, voice input, and meeting transcription.

  • WebSocket: wss://realtime.kittaai.com/v2/stt/live
  • Subprotocol: realtime.stt.msgpack.v1
  • Model: kitta-asr-realtime-v1
  • Audio: mono PCM16 at 16 kHz; send 3,200-byte chunks every 100 ms
  • Maximum session: 600 seconds
  • Price: 200 credits for each started minute

Call GET https://realtime.kittaai.com/v2/stt/capabilities for the current contract. The complete Open API catalog remains available at GET /api/openapi.json.

Server integration

Servers can authenticate during the WebSocket handshake:

Authorization: Bearer KITTA_API_KEY
import { readFile } from 'node:fs/promises';
import WebSocket from 'ws';

const ws = new WebSocket('wss://realtime.kittaai.com/v2/stt/live', 'realtime.stt.msgpack.v1', {
  headers: { Authorization: `Bearer ${process.env.KITTA_API_KEY}` },
});
const pcm = await readFile('speech-16khz-mono.pcm');

ws.on('open', () =>
  ws.send(
    JSON.stringify({
      event: 'start',
      request: {
        model: 'kitta-asr-realtime-v1',
        format: 'pcm',
        sample_rate: 16000,
        language_hints: ['en', 'zh'],
      },
    }),
  ),
);

ws.on('message', (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.event === 'ready') {
    for (let offset = 0; offset < pcm.length; offset += 3200)
      ws.send(pcm.subarray(offset, offset + 3200));
    ws.send(JSON.stringify({ event: 'stop' }));
  }
  if (event.event === 'transcript_partial') console.log('partial:', event.text);
  if (event.event === 'transcript_final') console.log('final:', event.text);
  if (event.event === 'usage') console.log('credits:', event.credits_used);
  if (event.event === 'error') throw new Error(`${event.code}: ${event.message}`);
});

Alternatively, omit the handshake authorization header and send the API key in the first JSON message:

{ "event": "auth", "token": "KITTA_API_KEY" }

Browser integration

Never embed a long-lived API key in browser code. Have your backend call POST https://realtime.kittaai.com/v2/stt/browser-tickets with the API key and the page's Origin header. The response contains a short-lived, single-use ticket, websocket_url, and subprotocol. After connecting, the browser sends the ticket in its first auth message, followed by start, binary PCM audio, and stop.

Events and settlement

The server emits authenticated, ready, transcript_partial, transcript_final, usage, and finish. Persist only transcript_final events or finish.transcript.

The gateway reserves the first billed minute before starting the provider and reserves each next minute before accepting its audio. On a normal stop or disconnect, it derives duration from received PCM bytes, settles credits server-side, and stores the transcript in history. A provider startup failure refunds the reservation. Insufficient balance returns quota_exceeded.

Use HTTP Speech to Text when you do not need streaming results.