Cursus
Ons overzichtsartikel over GPT-Realtime-2 besprak de lancering, de benchmark-claims en waarom OpenAI realtime spraak heeft opgesplitst in een kleine modelfamilie. Deze tutorial pakt de draad op waar dat artikel stopte: verbinden met de API, audio versturen en zien wat er in de code verandert.
De splitsing is in de praktijk belangrijk. Test 1 gebruikt gpt-realtime-whisper voor transcriptie, Test 2 gebruikt gpt-realtime-translate voor livevertaling en Test 3 gebruikt gpt-realtime-2 voor een stemassistent. Het hoofdmodel werkt ook voor eenvoudigere taken zoals vertalen of transcriberen, maar je betaalt dan voor een niveau van redeneren en antwoorden dat niet nodig is, dus dat is overkill.
De GPT-Realtime-2 API instellen in Python
Voor de tests is het handig om transport, authenticatie en audioformaat te scheiden. Die details blijven grotendeels gelijk in de voorbeelden. De model-endpoints en eventnamen zijn wat verandert wanneer het artikel van tekstoutput naar vertaalde spraak en vervolgens naar een volledige spraaklus gaat.
Kiezen tussen WebRTC en WebSocket
De OpenAI-docs geven één simpele regel: gebruik WebRTC voor browser- en mobiele clients, en gebruik WebSockets voor server-side applicaties. WebRTC handelt jitterbuffering en audiotransport af. WebSockets zijn logisch wanneer de backend al ruwe audio ontvangt van een telefonieprovider of mediapipeline.

Twee transportpaden voor de Realtime API. Afbeelding door de auteur.
Om die reden gebruiken alle drie de Python-tests WebSockets. Dat pad laat de eventnamen direct zien, zodat de modelverschillen zichtbaar zijn in de code. Voor een browserbuild gebruik je tijdelijke client secrets zodat je API-sleutel nooit de frontend bereikt.
Python-omgeving en -packages
Gebruik Python 3.9 of nieuwer. De volledige code voor alle vier de scripts is beschikbaar op github.com/KhalidAbdelaty/gpt-realtime-api. Clone het eerst en installeer daarna de dependencies:
git clone https://github.com/KhalidAbdelaty/gpt-realtime-api.git
cd gpt-realtime-api
pip install websocket-client sounddevice numpy python-dotenv
websocket-client verzorgt de socket. sounddevice neemt microfoonaudio op, numpy converteert de buffer en python-dotenv laadt de API-sleutel. Op macOS heb je mogelijk brew install portaudio nodig voordat sounddevice werkt. Op Linux installeer je portaudio19-dev.
Maak een .env-bestand in de root van je project:
OPENAI_API_KEY=sk-...
Laad het vervolgens in elk script:
import os
from dotenv import load_dotenv
load_dotenv()
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
Authenticatie en de WebSocket-URL
Server-side verbindingen gebruiken een Authorization: Bearer-header bij de WebSocket-handshake. Voeg OpenAI-Safety-Identifier toe als de app individuele gebruikers volgt. Dit zijn de paden die later in de tests worden gebruikt:
# Voice agent
wss://api.openai.com/v1/realtime?model=gpt-realtime-2
# Translation
wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate
# Transcription
wss://api.openai.com/v1/realtime?intent=transcription
Dat vertaalpad is later in Test 2 belangrijk, omdat het het enige endpoint is dat niet direct /v1/realtime gebruikt.
Test 1: Realtime audiotranscriptie met GPT-Realtime-Whisper
Transcriptie is een veelvoorkomende plek om te over-engineeren. Als de output alleen tekst is, is het transcriptiemodel voldoende.
Wat we testen
gpt-realtime-whisper neemt audio aan en geeft transcript-delta’s uit. Het redeneert niet, roept geen tools aan en spreekt niet terug. Die kleinere taak is de reden dat het wordt afgerekend op audioduur in plaats van met hetzelfde tokenmodel dat door gpt-realtime-2 wordt gebruikt. De kosten komen later terug in beeld.
Code-uitleg
Het sleutelveld is session.type: "transcription". Dat vertelt de API om assistent-antwoorden over te slaan en alleen transcript-events uit te sturen. Het volledige script handelt ook microfoonopname en threading af. Dit is het deel dat het Realtime-sessiegedrag verandert:
session_config = {
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {
"model": "gpt-realtime-whisper",
"language": "en"
},
"turn_detection": None
}
}
}
}
ws.send(json.dumps(session_config))
ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": audio_b64
}))
ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
Gebruik 24 kHz PCM16 mono-audio, gecodeerd als base64. Anders dan bij de voice-agent-sessie commit het script de inputbuffer handmatig op een timer in plaats van server_vad turndetectie te gebruiken. Lege commits geven input_audio_buffer_commit_empty, daarom commit het volledige script pas nadat er echte audio is verstuurd.

Transcript-delta’s komen woord voor woord in realtime binnen. Afbeelding door de auteur.
Waargenomen gedrag
In mijn lokale tests verschenen transcriptresultaten binnen het commitvenster, ruwweg 3 tot 4 seconden nadat de spraak begon. Omdat deze setup op handmatige commits vertrouwt in plaats van VAD, is de latentie gekoppeld aan het commit-interval.
Let ook op de volgorde: completion-events van overlappende beurten kunnen buiten volgorde aankomen, dus reconcile op item_id als je een UI rond de stream bouwt.

Transcriptie wordt getriggerd door een periodieke handmatige commit, niet door stilte-detectie. Afbeelding door de auteur.
Oordeel: geslaagd. Voor server-side live transcriptie deed gpt-realtime-whisper wat deze test nodig had. Ik zou nog steeds testen met echte microfoons, accenten en kamerruis voordat ik een latentiedoel stel.
Test 2: Realtime vertaling met GPT-Realtime-Translate
Live spraakvertaling lijkt in eerste instantie op transcriptie, maar de sessielevenstijd is anders. Het vertaal-endpoint haalt de assistent-antwoordlus weg, waardoor het voorbeeld korter is.
Wat we testen
Vertaalsessies hebben geen assistent-beurtlus en geen response.create. Het model werkt als een live tolk, niet als een conversational agent. Voor Q&A, tools of conversatiestatus schakelt het artikel in Test 3 over naar gpt-realtime-2.

Vertaalsessies gebruiken een eigen, apart endpoint. Afbeelding door de auteur.
Het model ondersteunt meer dan 70 invoertalen en 13 uitvoertalen. Je stelt het doel in met session.audio.output.language; brontaal-detectie is automatisch. De beperkingen zijn duidelijk: geen aangepaste prompting, geen stemkeuze en geen domeinglossaria.
Code-uitleg
Zoals eerder genoemd gebruikt vertaling de /translations WebSocket-URL. Twee andere details veranderen ook: het doelveld session.audio.output.language en de eventnaam session.input_audio_buffer.append. Let op het voorvoegsel session.. Vertaalsessies gebruiken dat hier.
url = "wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate"
session_config = {
"type": "session.update",
"session": {
"audio": {
"output": {"language": "es"},
"input": {
"transcription": {"model": "gpt-realtime-whisper"},
"noise_reduction": {"type": "near_field"}
}
}
}
}
ws.send(json.dumps(session_config))
ws.send(json.dumps({
"type": "session.input_audio_buffer.append",
"audio": audio_b64
}))
Vertaalde audio komt binnen op session.output_audio.delta, en de audiobytes staan in event["delta"], niet in event["audio"]. Het bron- en vertaalde transcript komen apart binnen:
if event_type == "session.output_audio.delta":
audio_out_queue.put(base64.b64decode(event["delta"]))
elif event_type == "session.input_transcript.delta":
print("[EN]", event.get("delta", ""), end="")
elif event_type == "session.output_transcript.delta":
print("[ES]", event.get("delta", ""), end="")
Eén randgeval: als de bronaudio al in de doeltaal is, kan het model stilte produceren in plaats van het door te geven.
Waargenomen gedrag
Bij korte Engelse-naar-Spaanse zinnen begon de vertaalde audio voordat de bronuiting klaar was. De terminal liet afwisselende [EN]- en [ES]-regels zien terwijl delta’s binnenstroomden. Meer uiteenlopende taalkoppels kunnen langer wachten op context. Ik kon de vertaalde stem zonder moeite volgen, maar aangepaste stemselectie is niet beschikbaar.
Oordeel: geslaagd, met een kanttekening. gpt-realtime-translate werkte voor directe livevertaling. Het is minder geschikt wanneer terminologiecontrole of stembranding belangrijk is.
Test 3: Een stemassistent bouwen met beurtwisseling en barge-in
Dit is de gpt-realtime-2-test: een stemagent die luistert, spreekt, context behoudt en tools kan aanroepen. Dit is ook het punt waarop de clientcode belangrijker wordt, omdat weergave en beurtstatus uit sync kunnen raken.
Wat we testen
gpt-realtime-2 is een spraak-naar-spraak redeneermodel. Het vermijdt een aparte STT-naar-LLM-naar-TTS-pijplijn, en het 128K-contextvenster geeft langere sessies meer ruimte. Redeneren wordt gestuurd met reasoning.effort; begin bij low tenzij de taak meer redeneren vereist, want hogere instellingen voegen latentie toe.
Code-uitleg
De setup hieronder gebruikt semantic_vad, dat naar spraaksignalen kijkt in plaats van alleen stilte. eagerness bepaalt hoe snel het model besluit dat de gebruiker klaar is. De onderdelen om op te letten zijn de modelnaam, audio-uitvoerinstellingen, handmatige response.create en de eventnaam voor assistentaudio:
session_config = {
"type": "session.update",
"session": {
"type": "realtime",
"model": "gpt-realtime-2",
"output_modalities": ["audio"],
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {"model": "gpt-realtime-whisper", "language": "en"},
"turn_detection": {
"type": "semantic_vad",
"eagerness": "medium",
"create_response": False,
"interrupt_response": True
}
},
"output": {
"format": {"type": "audio/pcm", "rate": 24000},
"voice": "marin"
}
},
"instructions": "You are a helpful voice assistant. Keep answers short.",
"reasoning": {"effort": "low"}
}
}
Wanneer het transcript van de gebruiker is voltooid, creëert de client de assistentrespons. Assistent-audio komt vervolgens binnen als response.output_audio.delta, niet als response.audio.delta.
if event_type == "conversation.item.input_audio_transcription.completed":
ws.send(json.dumps({"type": "response.create"}))
elif event_type == "response.output_audio.delta":
audio_out_queue.put(base64.b64decode(event["delta"]))
Barge-in afhandelen
De onderbrekingsvolgorde is makkelijk om fout te doen. Wanneer de gebruiker over de assistent heen praat, stuurt de server input_audio_buffer.speech_started. De client stopt de weergave, registreert hoeveel audio is afgespeeld en stuurt conversation.item.truncate met audio_end_ms om bij te houden waar het werd afgekapt. Anders blijft de server tekst transcriberen die de gebruiker nooit heeft gehoord, en kan de volgende beurt vreemd aanvoelen.
if current_response_item_id and playback_position_ms > 0:
ws.send(json.dumps({
"type": "conversation.item.truncate",
"item_id": current_response_item_id,
"content_index": 0,
"audio_end_ms": playback_position_ms
}))
Een praktisch probleem met laptopspeakers: de microfoon kan de audio-uitvoer van de assistent oppikken en terugsturen naar het model. Het voorbeeldscript gebruikt MUTE_MIC_DURING_ASSISTANT = True om de invoerstroom te dempen terwijl de assistent spreekt en voor een korte afkoelperiode erna. Zet dit alleen op False als je een koptelefoon gebruikt en onderbrekingsondersteuning wilt.

Truncatie houdt server en client in sync. Afbeelding door de auteur.
WebRTC en SIP handelen meer van deze buffering af. Met het WebSocket-pad dat in deze tutorial wordt gebruikt, ligt die verantwoordelijkheid bij de client. De teller in het voorbeeldscript is genoeg voor een demo; productiecode zou timestamps van de audio-uitvoerstream moeten bijhouden.
Preambles configureren voor redeneerlatentie
Wanneer reasoning.effort boven low staat, valt stilte op. Korte gesproken preambles kun je toevoegen in de systeemprompt:
# Preambles
Use a short spoken update before longer tasks.
Keep preambles under five seconds.
Skip preambles for short factual questions.
Dit gedrag is gedocumenteerd voor gpt-realtime-2.
Waargenomen gedrag
Beurtwisseling werkte met de standaardinstellingen in een stille kamer. Met laptopspeakers had ik mic-demping nodig; zonder dat hoorde het model zijn eigen output en begon een echolus.
In drukkere ruimtes waren VAD-instellingen en microfoonplaatsing belangrijker. Gespreksgeheugen bleef consistent tijdens een test van tien minuten, maar ik zou geen langere app uitbrengen zonder een herverbindingsplan.
Oordeel: geslaagd voor de kern van de spraaklus. gpt-realtime-2 kon in mijn test een assistent met lage redeneerinspanning aan. Het extra werk ligt bij de client: weergave, onderbrekingsafhandeling, herverbinden en tool-calls als de app die nodig heeft.
Streamlit-demo: alle drie de modellen in één interface
De Streamlit-app zet de tests achter een tabselector. Je kunt audio opnemen, een doeltaal kiezen en modelpaden vergelijken zonder scripts te bewerken. Ik heb dit als demo-app gehouden en niet als het hoofdleerspoor, aangezien de terminalscripts de events directer laten zien.

Drie modellen in één getabde interface. Afbeelding door de auteur.
De demovideo hieronder laat de tabs zien met een live API-sleutel. Elke tab gebruikt echte Realtime WebSocket-calls.
Voer de app uit vanuit dezelfde map als de scripts:
streamlit run demo_app.py
Je API-sleutel gaat in de zijbalk en wordt nergens opgeslagen. Voor een publieke app zet je hem in Streamlit Secrets.
GPT-Realtime-2: kosten en latentie in de praktijk
Zoals genoemd in Test 1, splitst de prijs in twee groepen: gpt-realtime-2 wordt afgerekend per token, terwijl vertaling en transcriptie per minuut worden afgerekend.

Token- en minutenaftrek per model. Afbeelding door de auteur.
Voor transcriptie en vertaling schalen de kosten met de duur. Op het moment van schrijven kost dertig minuten ongeveer $0,51 op gpt-realtime-whisper en ongeveer $1,02 op gpt-realtime-translate.
Stemassistenten zijn lastiger te schatten omdat audiotokens aan beide kanten van het gesprek oplopen. Sessielengte, spreekverhouding, redeneerinspanning en contextgrootte zijn allemaal van belang. Promptcaching kan kosten verlagen wanneer eerdere gespreksslagen stabiel blijven.
Een REST-transcriptiecall plus TTS is een andere vergelijking, tenzij live interactie vereist is. whisper-1 is goedkoper voor bestanden, maar het is niet hetzelfde type API.
GPT-Realtime-2 API-limieten, audiovereisten en troubleshooting
Dit zijn de limieten die mijn eerste testruns beïnvloedden. De meeste fouten kwamen door audio-opmaak of fouten in de sessielevenstijd, niet door het model zelf.
Transport- en audiobeperkingen
Zoals in de eerste test opgemerkt, moet WebSocket-audio PCM16 op 24 kHz, mono en base64-gecodeerd zijn. Elk input_audio_buffer.append-event is begrensd op 15 MB, dus chunks van 50 milliseconden blijven ruim onder de limiet. G.711 wordt ook ondersteund voor telefonie.
Realtime-sessies eindigen na 60 minuten op OpenAI en 30 minuten op Azure OpenAI. Langere apps hebben een herverbindingsplan nodig en een manier om state opnieuw op te bouwen. De stem moet ook vóór de eerste audio-uitvoer worden gekozen; je kunt niet midden in een sessie wisselen.
Ratelimieten en quota
Ratelimieten zijn tier-gebaseerd en project-specifiek. Tier 1 vermeldt momenteel 200 requests per minuut en 40.000 tokens per minuut voor gpt-realtime-2. De gratis tier wordt niet ondersteund.
Veelvoorkomende fouten en scherpe randjes
De fouten die ik het vaakst tegenkwam waren lege buffer-commits en onjuiste audio-opmaak. Let bij stemassistenten ook op feedbacklussen waarbij de microfoon de speakeroutput van de assistent hoort. Gebruik een koptelefoon, echo-onderdrukking of mic-demping.
Voor lange sessies: herverbind rond 55 minuten in plaats van te wachten op verlopen. Een documentatie-rimpel: de modelpagina van gpt-realtime-2 heeft een generieke rij "Streaming: Not supported", terwijl de Realtime-gidsen het gebruik van /v1/realtime documenteren. Die rij gaat over Chat Completions-streaming, niet over het gedrag van de Realtime API.
Eindoordeel
Hetzelfde patroon komt terug in de drie tests: elke taak heeft zijn eigen model en endpoint. Die splitsing beïnvloedt wat het model kan, hoe er wordt afgerekend en hoeveel clientcode je zelf moet beheren.
Zoals hierboven getoond dekt gpt-realtime-whisper live tekst, gpt-realtime-translate directe spraakvertaling en gpt-realtime-2 assistentgedrag met spraak, redeneren en context.
De code laat niet zien dat één model de andere vervangt. Het laat zien dat realtime spraakapps afhangen van sessieontwerp. Mijn startpunt zou het kleinste model zijn dat bij de taak past, met de resterende engineeringsinspanning op audiokwaliteit, beurtwisseling, herverbindingen en clientstate.
Voor meer achtergrond behandelen onze tutorials verwante audio- en realtime API-onderwerpen:
Ik ben een data-engineer en communitybouwer die werkt aan datapijplijnen, cloud en AI-tools, en tegelijkertijd praktische, impactvolle tutorials schrijft voor DataCamp en beginnende developers.

