OpenAI Realtime API Guide 2025: Low-Latency Voice & Audio
The OpenAI Realtime API is a WebSocket-based interface for building real-time voice applications powered by GPT-4o. Instead of a fragile TTS+STT pipeline, a single natively multimodal model handles audio input and output with sub-300ms latency — enabling natural interruption, emotion-aware responses, and function calling mid-conversation.
What Is the OpenAI Realtime API?
Traditional voice AI pipelines chain three separate models: a speech-to-text model (like Whisper) transcribes audio to text, an LLM generates a text response, and a TTS model converts that text back to speech. Each hop adds latency and loses information — the LLM never hears your tone, and the TTS voice sounds robotic.
The Realtime API eliminates the middlemen. GPT-4o natively processes audio in and audio out over a persistent WebSocket connection. It hears your actual voice, preserves emotional context, responds with a natural-sounding voice, and can be interrupted mid-sentence — all with end-to-end latency in the 300–500ms range.
Key capabilities:
- Bidirectional audio streaming — send audio chunks as PCM16 or base64, receive audio delta events in real time
- Server-side VAD — automatic voice activity detection; the server triggers responses when the user stops speaking
- Natural interruption handling — the model stops mid-response when the user speaks over it
- Function calling — tools work during voice conversations, enabling live data lookups
- Text + audio input/output — mix text and audio in the same session
Models & Pricing
Two models are available for the Realtime API, both accessed via the wss://api.openai.com/v1/realtime endpoint:
| Model | Audio input (per 1M tokens) | Audio output (per 1M tokens) | Best for |
|---|---|---|---|
gpt-4o-realtime-preview | $100 | $200 | Production voice assistants, high quality |
gpt-4o-mini-realtime-preview | $10 | $20 | Cost-sensitive apps, prototyping |
Audio tokens are measured by the audio duration: roughly 1,500 tokens per minute of audio input. A typical 5-minute voice conversation uses ~7,500 audio input tokens and ~10,000 audio output tokens.
WebSocket Connection
Connect to the Realtime API via a WebSocket with the model as a query parameter and your API key in the Authorization header. Once connected, send and receive JSON events over the socket.
Raw WebSocket connection
import websocket
import json
import os
url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview"
headers = [
f"Authorization: Bearer {os.environ['OPENAI_API_KEY']}",
"OpenAI-Beta: realtime=v1",
]
ws = websocket.WebSocketApp(
url,
header=headers,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close,
)
ws.run_forever()
After the connection opens, the server sends a session.created event. You can then configure the session, send audio, and receive responses.
Python Quickstart with the OpenAI SDK
The official OpenAI Python SDK (v1.50+) provides a high-level RealtimeConnection context manager that handles the WebSocket lifecycle, event routing, and audio chunking for you:
pip install openai
pip install "openai>=1.50.0"
realtime_quickstart.py
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def main():
async with client.beta.realtime.connect(
model="gpt-4o-realtime-preview"
) as conn:
# Configure the session
await conn.session.update(session={
"voice": "alloy",
"instructions": "You are a helpful assistant. Respond concisely.",
"turn_detection": {"type": "server_vad"},
})
# Send a text message (audio works the same way)
await conn.conversation.item.create(item={
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Say hello!"}],
})
await conn.response.create()
# Stream the response events
async for event in conn:
if event.type == "response.audio.delta":
# event.delta is base64-encoded PCM16 audio
audio_bytes = event.delta
# write to speaker or buffer here
print(f"Audio chunk received: {len(audio_bytes)} bytes")
elif event.type == "response.done":
print("Response complete")
break
asyncio.run(main()) JavaScript / Browser Quickstart
In the browser, use a native WebSocket combined with the MediaRecorder API to capture microphone input and stream it to the Realtime API. Note: for browser clients, use an ephemeral session token from your backend rather than exposing your API key.
Browser WebSocket + MediaRecorder pattern
// 1. Fetch ephemeral token from your backend
const tokenRes = await fetch("/api/realtime-token");
const { client_secret } = await tokenRes.json();
// 2. Connect via WebSocket using the ephemeral token
const ws = new WebSocket(
"wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview",
["realtime", `openai-insecure-api-key.${client_secret.value}`]
);
ws.onopen = () => {
// Configure session
ws.send(JSON.stringify({
type: "session.update",
session: {
voice: "shimmer",
instructions: "You are a helpful assistant.",
turn_detection: { type: "server_vad" },
}
}));
};
// 3. Capture microphone audio with MediaRecorder
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const recorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
recorder.ondataavailable = async (e) => {
const buffer = await e.data.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
ws.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: base64,
}));
};
recorder.start(100); // send chunks every 100ms
// 4. Handle incoming audio deltas
ws.onmessage = (e) => {
const event = JSON.parse(e.data);
if (event.type === "response.audio.delta") {
// Decode base64 PCM16 and play via AudioContext
playAudioChunk(event.delta);
}
};
Always generate ephemeral tokens server-side using POST /v1/realtime/sessions with your API key — never expose your API key in client-side code.
Session Configuration
Send a session.update event to configure voice, instructions, VAD settings, and tools. You can update the session at any time during the conversation.
session.update event — full config
{
"type": "session.update",
"session": {
// Voice options: alloy, echo, shimmer, verse, ash, ballad, coral, sage
"voice": "alloy",
// System prompt for the voice model
"instructions": "You are a friendly customer support agent for Acme Corp.",
// Input audio format: pcm16, g711_ulaw, g711_alaw
"input_audio_format": "pcm16",
// Output audio format: pcm16, g711_ulaw, g711_alaw
"output_audio_format": "pcm16",
// server_vad: automatic turn detection
// null: manual push-to-talk (use input_audio_buffer.commit)
"turn_detection": {
"type": "server_vad",
"threshold": 0.5, // VAD sensitivity (0.0-1.0)
"prefix_padding_ms": 300, // audio before speech start to include
"silence_duration_ms": 500 // silence before triggering response
},
// Modalities: ["text", "audio"] or ["text"] for text-only
"modalities": ["text", "audio"],
// Temperature for response generation
"temperature": 0.8,
// Max output tokens (audio + text combined)
"max_response_output_tokens": 4096
}
} Function Calling in Voice
Tools work in the Realtime API exactly like in Chat Completions — the model decides when to call a function and includes the call in its response stream. Add tools to the session config, then listen for response.function_call_arguments.done events:
Add tools to session
{
"type": "session.update",
"session": {
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g. San Francisco"
}
},
"required": ["location"]
}
}
],
"tool_choice": "auto"
}
} Handle function call and return result
ws.onmessage = async (e) => {
const event = JSON.parse(e.data);
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const callId = event.call_id;
// Execute your function
const result = await getWeather(args.location);
// Return the result to the model
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: callId,
output: JSON.stringify(result),
}
}));
// Ask the model to continue the conversation
ws.send(JSON.stringify({ type: "response.create" }));
}
}; Realtime API vs Traditional TTS + STT Pipeline
| Attribute | Realtime API | Whisper + GPT-4o + TTS |
|---|---|---|
| Latency | 300–500ms end-to-end | 1.5–4s (3 sequential API calls) |
| Voice quality | Natural, context-aware emotion | Robotic TTS, no emotion |
| Interruption handling | Built-in (VAD) | Manual, complex to implement |
| Cost | Higher per minute | Lower, more predictable |
| Complexity | Single WebSocket connection | 3 APIs, transcription step, error handling |
| Best for | Real-time voice UX, live agents | Transcription pipelines, async processing |
Use Cases
- Voice assistants — build a hands-free assistant with natural back-and-forth conversation, interruption support, and personality via the instructions field
- Customer service bots — automate inbound calls with function calling to query order status, CRM records, or ticketing systems in real time
- Real-time transcription + response — transcribe speech while simultaneously generating an AI response; use
response.audio_transcript.deltaevents for live captions - Language tutoring — have learners practice conversation with a patient, low-latency AI tutor that corrects pronunciation and grammar on the fly
- Accessibility tools — voice-controlled interfaces for users who cannot use a keyboard, with function calling to execute actions in your app
- Live interpretation — pair with translation instructions to create real-time speech-to-speech translation between languages
OpenAI's Realtime API Has Had Notable Outages
OpenAI's Realtime API has had notable outages. Prismix tracks OpenAI's status in real time — get alerted before your voice app breaks.
Monitor OpenAI Status →