Realtime transcription
wss://api.kittenml.com/v1/stt/realtime?model=emokittenasr-realtime
Use realtime STT when the words should appear while someone is still speaking. This is where EmoKitten moves beyond vanilla ASR: partial text arrives live, and the final event can preserve expression, pauses, and emphasis.
One WebSocket connection is one realtime session. Use a key with the
asr:transcribe permission. Authentication runs once when the connection
starts, not for every audio append.
Connect
Send the API key in the WebSocket upgrade request:
Authorization: Bearer sk_kitten_live_...
This header works in server-side WebSocket libraries. The browser's native
WebSocket constructor cannot set an Authorization header, so browser apps
need an authenticated backend relay that keeps the permanent key private.
For a browser microphone, use WebRTC transcription instead. The backend mints a short-lived client token, then microphone media travels by WebRTC rather than through a WebSocket relay.
The official OpenAI Realtime clients currently connect to /v1/realtime and
cannot override that path. Use a standard WebSocket client with KittenML's
explicit /v1/stt/realtime URL, as shown below.
Audio formats
The recommended format is headerless mono PCM16 at 24 kHz.
| Format | Sample rate | Session format value |
|---|---|---|
| Signed PCM16 little-endian | 16 or 24 kHz | audio/pcm, pcm16, or s16le |
| Float32 little-endian | 16 or 24 kHz | pcm_f32le or f32le |
| G.711 µ-law | 8 kHz | g711_ulaw or audio/pcmu |
| G.711 A-law | 8 kHz | g711_alaw or audio/pcma |
FLAC, WAV, MP3, and other file containers are not raw realtime audio. Decode them before appending bytes, or use the upload endpoint.
Send 100–200 ms client appends for responsive live audio; the runnable examples use 200 ms. Client append size and transcript update cadence are separate: one append does not guarantee one transcript event.
Think of appends as feeding the stream, not asking the model to speak after every bite.
Session lifecycle
- Connect and wait for
session.created. - Send
session.updateand wait forsession.updated. - Send base64 audio in
input_audio_buffer.appendevents. - Read transcript deltas while audio continues.
- Send
input_audio_buffer.commitwhen the manual turn is finished. - Use
conversation.item.input_audio_transcription.completedas the final transcript. - Continue with another turn, or send
session.closeand close the socket.
input_audio_buffer.commit finalizes the current turn; it does not close the
WebSocket.
Configure the session
{
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {
"model": "emokittenasr-realtime",
"delay": "medium"
},
"turn_detection": null
}
}
}
}
transcription.delay selects the target cadence for partial model decoding.
It is separate from the recommended 100–200 ms transport append size:
| Value | Target partial cadence |
|---|---|
minimal | 500 ms |
low | 750 ms |
medium | 1,000 ms (default) |
high | 1,500 ms |
xhigh | 2,000 ms |
Set turn_detection to null for explicit commits. For automatic speech
turns, send a complete update such as:
{
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {"model": "emokittenasr-realtime"},
"turn_detection": {
"type": "server_vad",
"threshold": 0.3,
"prefix_padding_ms": 350,
"silence_duration_ms": 700
}
}
}
}
}
Understand server VAD settings
These settings apply only when turn_detection.type is server_vad. The
defaults shown below are used when you enable server_vad and omit an
individual setting.
| Setting | Default | Allowed range | If you increase it | If you decrease it |
|---|---|---|---|---|
threshold | 0.3 | 0.01–1.0 | Makes detection less sensitive. It rejects more background noise but may miss quiet speech. | Makes detection more sensitive. It detects quieter speech but may mistake noise for speech. |
prefix_padding_ms | 350 ms | 0–5,000 ms | Keeps more audio from before speech was detected, protecting initial syllables but including more leading silence or noise. | Keeps less preceding audio, but increases the chance of clipping the beginning of the first word. |
silence_duration_ms | 700 ms | 100–10,000 ms | Allows longer pauses within one turn, reducing accidental splits but delaying the final transcript. | Finalizes turns faster, but may split a sentence when the speaker pauses briefly. |
In practical terms, threshold controls speech-detection sensitivity,
prefix_padding_ms prevents the beginning of speech from being clipped, and
silence_duration_ms determines how long the server waits after speech stops
before automatically committing the turn. With the default
silence_duration_ms, endpoint detection contributes approximately 700 ms of
waiting after the speaker becomes silent, in addition to network and inference
time.
Start with the defaults. Raise threshold in a noisy environment, lower it for
consistently quiet speakers, and adjust silence_duration_ms only when you need
faster finalization or greater tolerance for pauses.
Append audio and commit
{"type":"input_audio_buffer.append","audio":"<base64 audio bytes>"}
{"type":"input_audio_buffer.commit"}
Python example
Install websockets and python-dotenv:
python3 -m pip install websockets python-dotenv
The complete, runnable client is in the KittenML examples repository. It sends paced 200 ms PCM appends while reading transcript events concurrently.
JavaScript example
Install ws:
npm install ws
See the complete Node.js realtime client.
Client events
| Event | Purpose |
|---|---|
session.update | Configure model, audio, language, and turn detection |
input_audio_buffer.append | Append base64-encoded audio |
input_audio_buffer.commit | Finalize the current manual turn |
input_audio_buffer.clear | Discard uncommitted buffered audio |
session.close | End the session |
ping | Check the connection |
Server events
| Event | Purpose |
|---|---|
session.created | Session is allocated |
session.updated | Configuration was accepted |
conversation.item.input_audio_transcription.delta | Provisional transcript snapshot |
input_audio_buffer.speech_started | Server VAD detected the start of speech |
input_audio_buffer.speech_stopped | Server VAD detected the end of speech |
input_audio_buffer.committed | A turn was committed |
input_audio_buffer.cleared | Uncommitted audio was discarded |
conversation.item.input_audio_transcription.completed | Authoritative final transcript |
pong | Response to a client ping |
session.closed | Session ended |
error | The request or session failed |
See Realtime events for complete event fields. See Language hints for accepted language values.
Limits
- Realtime and upload STT share five active ASR slots per organization.
- A realtime session holds one slot until it closes.
- Realtime sessions have no fixed duration limit. Keep the connection open only while it is useful and close it explicitly when finished.
- A decoded append may be at most 15 MiB; a WebSocket message may be at most 24 MiB.
See Rate limits and timeouts for 429 handling.