wss://api.aircaps.com/v1/realtime
Protocol
1
Connect
Open the WebSocket with your key in the handshake header:
Authorization: Bearer aircaps_sk_...2
Send start
The first message is JSON.
language accepts only en; multilingual support is planned.3
Wait for ready
The server may send
provider_status messages while the session starts, then ready. Do not send audio before ready.4
Stream audio
Send binary frames of mono 16 kHz signed 16-bit little-endian PCM, in real time. 20 ms frames (640 bytes) are recommended; frames may be at most 64 KB. Send
{"type": "ping"} to keep the connection open while you have no audio to send.5
Finish
Send
{"type": "finish"}. The server sends the final transcript, then a finished message, and closes.Messages from the server
text is always the complete transcript so far; replace what you display rather than appending.
For long sessions, set "transcript_text": "incremental" in start so messages stay small. Each transcript then carries an edit instead of text: remove text_delete characters from the end of the text you have, then append text_append. The result is always the complete transcript so far.
Key terms
Key terms bias recognition toward names and jargon. Send up to 64 incustom_vocabulary, each with a text (up to 100 characters) and a strength from 0 to 12:
Speaker labels
Set"diarization": true in start to label who said what, live. Every transcript keeps the complete text and adds utterances and speakers:
- Utterances with
is_final: truebecame final since the previous message. Each is sent once and never changes: keep it. - Utterances with
is_final: falseare pending and may still change (their text, speaker or boundaries). Replace the previous pending ones each time. - Show the kept final utterances and the latest pending ones, ordered by
start.
speaker_0, speaker_1, … in order of first speech, as in async transcripts, and stay the same for the whole session. Words get their speaker about a second after they are spoken; until then, a pending utterance may show a provisional label, or null before anyone has been recognized. Overlapping speech comes out as interleaved utterances.
diarization_unavailable (close code 1013); retry, or start without diarization.
Speaker names
With AirCaps Speaker ID-v1 enabled on your account, a live session can also put names on its speakers. Enroll the people you expect to hear, then addspeaker_identification to start, together with "diarization": true (names need speaker labels):
"speaker_identification": true uses both defaults. ready confirms it with "speaker_identification": {"model": "aircaps-speaker-id-v1", "threshold": 0.6, "speaker_ids": [...], "speakers_compared": 2} (speaker_ids is null when comparing with every enrolled speaker).
Names arrive as speaker_identification messages, one for each stretch of a speaker’s speech, named or not:
speaker_id and speaker_name: the decision for the same speaker that overlaps them most, as known when the utterance is sent (null until then). Final utterances are never resent, so to name one after the fact, apply later speaker_identification messages the same way:
- Labels stay
speaker_0,speaker_1, …; names are added next to them. - An unnamed decision still lists its closest
matches(up to 3), so you can apply your own rule.reasonsays why:accepted,score(best score not abovethreshold),margin(too close to the next-best), orerror(this stretch could not be compared; the session goes on). - Two speakers can get the same name, for example when one person is briefly split into two speakers.
- No extra usage: the session counts its audio duration, as always.
start with speaker_identification ends with model_access_denied (4403); without "diarization": true, with invalid_start (4400). The named speaker_ids must exist and have at least one enrollment (speaker_not_found, speaker_not_enrolled), and without speaker_ids your account needs at least one enrolled speaker (no_enrolled_speakers); these close with 4400. If speaker names are briefly unavailable, the session ends with speaker_identification_unavailable (1013); retry, or start without speaker_identification. Voice data rules apply: see Privacy and consent.
Rules
- Send the
startmessage within 45 seconds of connecting. It accepts only the documented fields. - Audio must arrive in real time; sending faster (beyond a 2-second burst) ends the session with
realtime_rate_exceeded. - A session can last up to 30 minutes (
session_limit); open a new one to continue. Longer sessions, up to 4 hours, are available on request (talk to sales). Your account’s session length ismax_session_secondsinGET /v1/account. - No messages (audio or
ping) for 45 seconds ends the session withidle_timeout. - At most 5 sessions per account at once.
Errors
Every session that ends abnormally sends anerror message with a code, then closes with the matching WebSocket close code.
A normal session ends with
finished and close code 1000.
Retrying
Close code 1013 means try again, whether the session was starting or already streaming: open a new session, waiting longer between attempts (for example 1 s, 2 s, 4 s … up to 30 s, with some jitter). Do the same if the connection can’t be opened (for example a handshake timeout, or a refused or reset connection) or drops without a close message. A session that ends early counts only the audio you streamed. A connection can also go quiet without closing. To notice: if nothing arrives from the server for 30 seconds, send{"type": "ping"}; if no pong arrives within 10 seconds, close the connection and open a new session as above.
See the Python sample.
