Realtime Speech to Text
Transcribe live microphone audio into text in real time using streaming ASR models.
Overview
Realtime speech-to-text models convert streaming audio into text transcripts as the user speaks. Unlike Realtime Voice Conversation which produces two-way audio, this mode is audio in → text out only. There is no AI audio playback — the server returns transcript strings over the WebSocket.
The flow is:
- Run the realtime STT model via POST /Run to get a
socketaccesstoken - Connect to the WebSocket and send
task_infowith your token - Wait for
task_stream_ready— the model is ready to receive audio - Stream microphone audio as binary frames (client → server)
- Receive transcript text as
task_outputmessages withTRANSCRIPT_USER:prefix - End the session with
task_session_end
Key difference from Voice Conversation: No binary audio is sent back from the server. All server → client messages are JSON text events.
How It Differs from Realtime Voice Conversation
| Realtime Voice Conversation | Realtime Speech to Text | |
|---|---|---|
| Input | Microphone audio (streamed) | Microphone audio (streamed) |
| Output | AI audio + transcripts | Transcript text only |
| Direction | Bidirectional (client ↔ server) | Client → server audio, server → client text |
| Binary frames from server | Yes (AI audio) | No (all server messages are JSON) |
| Transcripts | TRANSCRIPT_USER: and TRANSCRIPT_AI: |
TRANSCRIPT_USER: only |
| Use case | Interactive voice chat | Live dictation, captioning, meeting transcription |
Run Parameters
Each realtime STT model has its own set of parameters. Use POST /Tool/Detail to discover the exact parameters for a specific model. See Model Parameters for parameter types.
Browse available realtime STT models on the models page (filter by Realtime STT category). Common parameters typically include a transcription delay setting that trades speed for accuracy; some models also take a language. Example run for Voxtral Mini 4B Realtime:
curl -X POST "https://api.wiro.ai/v1/Run/mistralai/voxtral-mini-4b-realtime-2602" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"transcriptionDelay": "balanced"
}'
Important: Parameters vary per model. Always check the model's detail page or use /Tool/Detail to get the exact parameter list before integrating. The run response returns a socketaccesstoken used to subscribe to the WebSocket.
Connection & Registration
After running the task, connect to the WebSocket and register with task_info:
var ws = new WebSocket("wss://socket.wiro.ai/v1");
ws.onopen = function() {
ws.send(JSON.stringify({
type: "task_info",
tasktoken: "YOUR_SOCKET_ACCESS_TOKEN"
}));
};
Note: Both standard and realtime models use type: "task_info" with tasktoken to register on the WebSocket.
Realtime Events
During a realtime speech-to-text session, you'll receive these WebSocket events:
| Event | Direction | Description |
|---|---|---|
task_stream_ready |
server → client | Session is ready — start sending microphone audio |
task_output |
server → client | Transcript text prefixed with TRANSCRIPT_USER: |
task_stream_end |
server → client | Transcription stream has ended — no more transcripts will arrive |
task_cost |
server → client | Cost update — includes turnCost, cumulativeCost, and usage breakdown |
task_end |
server → client | The model process has exited. Post-processing follows — wait for task_postprocess_end before closing. |
task_postprocess_end |
server → client | Post-processing complete — safe to close the WebSocket now. |
Unlike voice conversation, there are no binary audio frames from the server. Every server message is a JSON text event.
Audio Format
Audio flows in one direction only: client → server.
| Property | Value |
|---|---|
| Format | PCM (raw, uncompressed) |
| Bit depth | 16-bit signed integer (Int16) |
| Sample rate | 24,000 Hz (24 kHz) |
| Channels | Mono (1 channel) |
| Byte order | Little-endian |
| Chunk size | 4,800 samples (200 ms) = 9,600 bytes |
The server internally resamples to the model's native rate (e.g. 16 kHz for Voxtral). Always send at 24 kHz — the server handles conversion.
Binary Frame Format
Every binary WebSocket frame sent from the client is structured as:
[tasktoken]|[PCM audio data]
The pipe character | (0x7C) separates the token from the raw audio bytes. There are no binary frames from the server — all responses are JSON text.
Sending Microphone Audio
Capture microphone audio at 24 kHz using the Web Audio API with an AudioWorklet. Convert Float32 samples to Int16, prepend your task token, and send as a binary frame.
Key steps:
- Request microphone with
getUserMedia(enable echo cancellation and noise suppression) - Create an
AudioContextat 24,000 Hz sample rate - Use an AudioWorklet to buffer and convert samples to Int16
- Send each chunk as
tasktoken|pcm_databinary frame - Continue sending until you end the session
Tip: You can send audio continuously — the model handles silence detection and only returns transcripts when speech is detected.
Transcripts
Transcripts arrive as task_output messages with the TRANSCRIPT_USER: prefix. Each message contains a segment of transcribed speech:
{
"type": "task_output",
"message": "TRANSCRIPT_USER:What's the weather like today?"
}
{
"type": "task_output",
"message": "TRANSCRIPT_USER:I need to book a flight to New York."
}
Progressive Results
Transcripts arrive progressively as words and phrases are recognized — not just at segment boundaries. The model streams TRANSCRIPT_USER: messages word-by-word as speech is detected, so the client can display live, incremental results. To build a full transcript, concatenate all received messages:
var fullTranscript = [];
// Inside your message handler
if (msg.type === 'task_output' &&
typeof msg.message === 'string' &&
msg.message.startsWith('TRANSCRIPT_USER:')) {
var segment = msg.message.substring(16);
fullTranscript.push(segment);
console.log('Segment:', segment);
console.log('Full:', fullTranscript.join(' '));
}
Note: Unlike voice conversation, there is no TRANSCRIPT_AI: prefix. All transcripts are user speech.
Ending a Session
To gracefully end a realtime session, send task_session_end:
{
"type": "task_session_end",
"tasktoken": "YOUR_SOCKET_ACCESS_TOKEN"
}
After sending this, the server processes any remaining buffered audio, sends final transcript and cost events, and then emits task_postprocess_end. Wait for task_postprocess_end before closing the WebSocket.
Safety: If the client disconnects without sending task_session_end, the server automatically terminates the session to prevent the pipeline from running indefinitely (and the provider from continuing to charge). Always send task_session_end explicitly for a clean shutdown.
Insufficient balance: The session is charged turn by turn as it runs. When a charged turn uses up your available balance (the team wallet's balance when you use a team project's API key), the server stops the session right after that turn. That turn is charged in full, and so is any final usage reported while the session closes, so your balance can end slightly below zero. You still receive the final task_cost and task_end events.