Realtime speech
Realtime runs a speech-to-speech conversation over a single WebSocket — audio in, audio out, with the model answering as you speak. MirAPI exposes it at /v1/realtime in the OpenAI Realtime format, so clients and SDKs written for the OpenAI Realtime API work after you point them at the MirAPI endpoint.
Connect
Section titled “Connect”/v1/realtime opens a WebSocket connection — it is not a plain HTTP call. The client sends an HTTP upgrade request, and the server answers 101 Switching Protocols before any events flow.
| Element | Value |
|---|---|
| URL | https://api.mirapi.ai/v1/realtime?model=<realtime-model> — use the wss:// scheme |
| Auth | Authorization: Bearer <key> header on the WebSocket handshake |
| Model | A realtime-capable model from the catalogue (see “Pick a model”) |
MirAPI follows the OpenAI-compatible base URL convention, so the endpoint is https://api.mirapi.ai/v1/realtime. If your client cannot set custom headers on the handshake, use one that can — the key must be sent in the standard bearer header.
Verify the handshake
Section titled “Verify the handshake”A WebSocket handshake is an HTTP upgrade request. You can verify connectivity without a full client by sending the upgrade headers yourself:
curl -i -N "https://api.mirapi.ai/v1/realtime?model=deepseek-chat" \ -H "Authorization: Bearer $MIRAPI_API_KEY" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ=="A 101 Switching Protocols response means the socket is open. Any other status is a rejection — see “Errors and retries” for the codes.
Session events
Section titled “Session events”Once the socket is open, both sides exchange JSON events from the OpenAI Realtime protocol. MirAPI relays the same events unchanged:
| Event | What it does |
|---|---|
session.update |
Configure the session, for example its modalities |
conversation.item.create |
Add an item to the conversation |
input_audio_buffer.append |
Stream audio chunks into the input buffer |
For the full message set, follow the OpenAI Realtime API documentation.
Python example
Section titled “Python example”Any WebSocket client that speaks the protocol works. This example uses websockets to connect, configure the session, and read the first reply:
import asyncioimport osimport websockets
async def main(): url = "https://api.mirapi.ai/v1/realtime?model=deepseek-chat".replace("https://", "wss://") headers = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}
async with websockets.connect(url, additional_headers=headers) as ws: # Configure the session, then stream audio in and out. await ws.send('{"type": "session.update", "session": {"modalities": ["audio", "text"]}}') reply = await ws.recv() print(reply)
asyncio.run(main())Replace deepseek-chat with a realtime-capable model before running.
Pick a model
Section titled “Pick a model”/v1/realtime requires a model that can output audio in real time. Find one with GET /v1/models — look for audio in the entry’s output_modalities — and send its exact id as the model query parameter:
curl https://api.mirapi.ai/v1/models \ -H "Authorization: Bearer $MIRAPI_API_KEY"A text-only model cannot open a realtime session: it is rejected because it cannot produce the audio output the session expects. Catalogue entries marked Coming soon cannot be called yet.
Billing and logs
Section titled “Billing and logs”Realtime sessions are billed like any other request, and charges accrue as the session runs. As with other endpoints, the API does not return a monetary amount — reconcile costs in three places:
- the console billing log,
GET /api/usage/tokenfor the account-level summary,GET /api/log/tokenfor per-request detail.
If the prepaid balance runs out, the request is rejected with 403; top up and the same key works again immediately.
Errors and retries
Section titled “Errors and retries”Failures before the socket opens arrive as HTTP status codes on the handshake:
| Status | Meaning | Action |
|---|---|---|
| 401 | Key missing or invalid | Fix the credentials |
| 403 | Valid key but rejected — balance, whitelist, quota, or IP | Resolve the cause and retry |
| 413 | Request body too large | Reduce the payload |
| 429 | Rate limited | Exponential backoff with jitter; no Retry-After header |
| 500 | Gateway or upstream failure | Retry idempotent requests |
MirAPI does not retry on your behalf. Every error message ends with a request ID — include it when you open a ticket.
Related links
Section titled “Related links”- Speech & audio — transcription, translation, and text-to-speech endpoints
- Configuration — authentication and base URLs
- Models & pricing — find a realtime-capable model in the catalogue
- Errors — status codes, envelopes, and request IDs