DEV Community

Cover image for How to use the GPT-Realtime-2.1-mini API
Hassann
Hassann

Posted on • Originally published at apidog.com

How to use the GPT-Realtime-2.1-mini API

Voice agents used to require three separate stages: speech-to-text, a language model, and text-to-speech. Each hop added latency and stripped away some tone. OpenAI’s Realtime API combines that pipeline into a single speech-to-speech model, and gpt-realtime-2.1-mini is the lower-cost, lower-latency mini tier. It listens to audio, reasons over the conversation, and streams spoken responses over one connection.

Try Apidog today

This guide shows how to call gpt-realtime-2.1-mini end to end: which model ID to use, how to connect with WebSocket and WebRTC, how to configure a session, how to add tools, and how to test the workflow with Apidog before wiring it into an app. The implementation maps to the official OpenAI Realtime guide.

First, get the model name right

There are two common identifiers for the mini realtime model:

  • gpt-realtime-2.1-mini: the versioned ID. This appears on the OpenAI pricing page and pins you to the 2.1 generation.
  • gpt-realtime-mini: the family alias. It points to the latest mini snapshot, currently gpt-realtime-mini-2025-12-15.

Use aliases while prototyping. Pin a dated snapshot before production if you need stable behavior.

Identifier What it points to
gpt-realtime-mini Latest mini snapshot, auto-updates
gpt-realtime-2.1-mini The 2.1-generation mini model
gpt-realtime-mini-2025-12-15 Pinned snapshot, current
gpt-realtime-mini-2025-10-06 Pinned snapshot, previous

Recommended workflow:

  1. Build with gpt-realtime-mini or gpt-realtime-2.1-mini.
  2. Test your prompts, tools, turn detection, and audio behavior.
  3. Pin a snapshot such as gpt-realtime-mini-2025-12-15 before release.

Realtime model identifiers

What gpt-realtime-2.1-mini does

gpt-realtime-2.1-mini is a speech-to-speech model. You stream audio in, and it streams audio back with natural intonation. You do not need to run a separate transcription model or TTS model.

It also supports text, so you can mix typed messages, spoken input, audio output, and transcripts in the same session.

From the model page:

Property Value
Input modalities Text, image, audio
Output modalities Text, audio
Context window 32,000 tokens
Max output 4,096 tokens
Connections WebRTC, WebSocket, SIP
Voices alloy, ash, ballad, coral, echo, sage, shimmer, verse, marin, cedar

marin and cedar are the newest voices and are exclusive to the Realtime API. OpenAI recommends them for the most natural output. The older voices still work if you need a specific sound.

Use the mini tier when you want:

  • Lower latency
  • Lower cost
  • Voice support bots
  • Order-taking flows
  • Voice front-ends for apps
  • Lightweight interactive agents

Use the full gpt-realtime-2.1 model when the conversation needs heavier reasoning.

What it costs

Mini is significantly cheaper than the full realtime model. Current token rates from the pricing page:

Model Text input Cached input Audio input Audio output
gpt-realtime-2.1-mini $0.60 / 1M $0.30 / 1M $10 / 1M $20 / 1M
gpt-realtime-2.1 $4.00 / 1M $0.40 / 1M $32 / 1M $64 / 1M

Audio output is usually the dominant cost. The more your agent talks, the more you pay.

A practical optimization is to control verbosity in your session instructions:

Keep answers to one or two short sentences unless the user asks for detail.
Enter fullscreen mode Exit fullscreen mode

Real-world per-minute costs for mini often land around $0.06 to $0.15, depending on how much the agent speaks. Always confirm against the live pricing page before forecasting production spend.

Prerequisites

You need:

  1. An OpenAI API key with Realtime access.
  2. The key available server-side as OPENAI_API_KEY.
  3. Node.js 18+ for the examples.
  4. The ws package if you want to test raw WebSocket.
  5. HTTPS or localhost for browser microphone access with getUserMedia.

Install the WebSocket dependency:

npm install ws
Enter fullscreen mode Exit fullscreen mode

Important rule:

Never expose your real OpenAI API key in browser or mobile client code.
Enter fullscreen mode Exit fullscreen mode

Browser and mobile apps should use short-lived ephemeral tokens. Your server creates those tokens with the real API key, then sends the temporary token to the client.

Pick a connection method

gpt-realtime-2.1-mini supports WebRTC, WebSocket, and SIP.

Transport Use it when Auth
WebRTC Audio is captured or played in a browser or mobile app Ephemeral client secret
WebSocket Your server handles raw audio or you want a fast prototype API key, server-side
SIP You are connecting a phone or telephony system API key

A practical implementation path:

  1. Start with WebSocket to confirm model access and event flow.
  2. Add audio output.
  3. Move to WebRTC when you need browser microphone input and low-latency playback.
  4. Add tools once the voice loop works.

Quickstart 1: WebSocket from your server

WebSocket is the fastest way to verify that your account, model ID, and event handling work.

Endpoint:

wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-mini
Enter fullscreen mode Exit fullscreen mode

The GA interface uses a normal Authorization: Bearer header. You do not need the old OpenAI-Beta header.

Create realtime-text.js:

import WebSocket from "ws";

const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-mini";

const ws = new WebSocket(url, {
  headers: {
    Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
  },
});

ws.on("open", () => {
  // 1. Configure the session
  ws.send(
    JSON.stringify({
      type: "session.update",
      session: {
        type: "realtime",
        model: "gpt-realtime-2.1-mini",
        output_modalities: ["text"],
        instructions:
          "You are a concise API support agent. Keep answers short.",
      },
    })
  );

  // 2. Add a user message
  ws.send(
    JSON.stringify({
      type: "conversation.item.create",
      item: {
        type: "message",
        role: "user",
        content: [
          {
            type: "input_text",
            text: "What is an idempotent request?",
          },
        ],
      },
    })
  );

  // 3. Ask the model to respond
  ws.send(
    JSON.stringify({
      type: "response.create",
    })
  );
});

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());

  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }

  if (event.type === "response.done") {
    ws.close();
  }
});

ws.on("error", console.error);
Enter fullscreen mode Exit fullscreen mode

Run it:

OPENAI_API_KEY=your_api_key node realtime-text.js
Enter fullscreen mode Exit fullscreen mode

The basic Realtime flow is:

  1. Send session.update.
  2. Add user input with conversation.item.create.
  3. Trigger generation with response.create.
  4. Listen for streaming events.
  5. Stop when you receive response.done.

Common server events:

Event Meaning
session.created Session was created
session.updated Your session config was accepted
response.output_text.delta Text output chunk
response.output_audio.delta Base64 audio output chunk
response.output_audio_transcript.delta Transcript of spoken output
response.done Turn is complete

Switch the WebSocket example to audio output

To receive audio instead of text, update the session:

ws.send(
  JSON.stringify({
    type: "session.update",
    session: {
      type: "realtime",
      model: "gpt-realtime-2.1-mini",
      output_modalities: ["audio"],
      instructions:
        "You are a concise voice support agent. Keep answers short.",
      audio: {
        output: {
          format: {
            type: "audio/pcm",
            rate: 24000,
          },
          voice: "marin",
        },
      },
    },
  })
);
Enter fullscreen mode Exit fullscreen mode

Then listen for audio deltas:

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());

  if (event.type === "response.output_audio.delta") {
    const audioChunk = Buffer.from(event.delta, "base64");

    // Send this PCM chunk to your playback pipeline,
    // media server, file writer, or audio stream.
    console.log("Received audio bytes:", audioChunk.length);
  }

  if (event.type === "response.output_audio_transcript.delta") {
    process.stdout.write(event.delta);
  }

  if (event.type === "response.done") {
    ws.close();
  }
});
Enter fullscreen mode Exit fullscreen mode

For a production server-side voice pipeline, route those PCM chunks into your media layer.

Quickstart 2: WebRTC in the browser

Use WebRTC when the browser captures microphone input and plays the model’s audio response directly.

The authentication pattern is:

  1. Browser asks your server for an ephemeral token.
  2. Server calls OpenAI with your real API key.
  3. Server returns the short-lived token to the browser.
  4. Browser connects to the Realtime API with that ephemeral token.

Step 1: Mint an ephemeral token on your server

Example server-side code:

const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    session: {
      type: "realtime",
      model: "gpt-realtime-2.1-mini",
    },
  }),
});

const { value } = await r.json();

// Send `value` to the browser.
// It is an ephemeral key and starts with "ek_".
Enter fullscreen mode Exit fullscreen mode

Do not persist this token. Mint a fresh one per session.

Step 2: Connect from the browser

Add an audio element to your page:

<audio id="audio" autoplay></audio>
Enter fullscreen mode Exit fullscreen mode

Then create the WebRTC connection:

// browser side: EPHEMERAL_KEY came from your server
const pc = new RTCPeerConnection();

// Play the model's remote audio stream
pc.ontrack = (event) => {
  document.getElementById("audio").srcObject = event.streams[0];
};

// Capture and send microphone audio
const mic = await navigator.mediaDevices.getUserMedia({
  audio: true,
});

pc.addTrack(mic.getTracks()[0]);

// Use a data channel for Realtime API events
const channel = pc.createDataChannel("oai-events");

channel.onmessage = (event) => {
  console.log(JSON.parse(event.data));
};

// Create SDP offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

// Exchange SDP with the Realtime API
const sdpResp = await fetch(
  "https://api.openai.com/v1/realtime/calls?model=gpt-realtime-2.1-mini",
  {
    method: "POST",
    body: offer.sdp,
    headers: {
      Authorization: `Bearer ${EPHEMERAL_KEY}`,
      "Content-Type": "application/sdp",
    },
  }
);

await pc.setRemoteDescription({
  type: "answer",
  sdp: await sdpResp.text(),
});
Enter fullscreen mode Exit fullscreen mode

Once connected:

  • The model listens to the microphone track.
  • The model speaks through pc.ontrack.
  • JSON events flow through the oai-events data channel.

You can send the same event payloads used in the WebSocket example:

channel.send(
  JSON.stringify({
    type: "session.update",
    session: {
      type: "realtime",
      model: "gpt-realtime-2.1-mini",
      output_modalities: ["audio"],
      instructions:
        "You are a friendly booking assistant. Confirm details before acting.",
      audio: {
        output: {
          voice: "marin",
        },
      },
    },
  })
);
Enter fullscreen mode Exit fullscreen mode

Shape the session

The session object controls model behavior, output format, voice, and turn detection.

Here is a full audio session update:

{
  type: "session.update",
  session: {
    type: "realtime",
    model: "gpt-realtime-2.1-mini",
    output_modalities: ["audio"],
    instructions: "You are a friendly booking assistant. Confirm details before acting.",
    audio: {
      input: {
        format: {
          type: "audio/pcm",
          rate: 24000
        },
        turn_detection: {
          type: "semantic_vad"
        }
      },
      output: {
        format: {
          type: "audio/pcm",
          rate: 24000
        },
        voice: "marin"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Key fields:

Field What to configure
instructions Persona, behavior, guardrails, and verbosity
output_modalities Use ["audio"] for voice or ["text"] for transcript-only output
audio.output.voice Use marin, cedar, or another supported voice
audio.input.turn_detection Use semantic_vad for more natural turn-taking or server_vad for silence-based detection

You can update the session mid-call by sending another session.update. You do not need to reconnect.

Add tools so the agent can act

A voice agent becomes useful when it can call tools: check an order, book a table, look up inventory, or trigger a workflow.

Realtime uses the same function-calling pattern as the rest of the OpenAI platform:

  1. Declare tools in the session.
  2. Let the model decide when to call a tool.
  3. Listen for the function-call event.
  4. Run your application code.
  5. Send the result back into the conversation.
  6. Trigger the next response.

If you have used tools with the chat API, the mental model is the same. For deeper schema examples, see OpenAI function calling and structured outputs.

At a high level, your event handler should look like this:

ws.on("message", async (raw) => {
  const event = JSON.parse(raw.toString());

  if (event.type === "response.function_call_arguments.done") {
    const args = JSON.parse(event.arguments);

    // Run your application logic
    const result = await lookupOrder(args.order_id);

    // Send the tool result back
    ws.send(
      JSON.stringify({
        type: "conversation.item.create",
        item: {
          type: "function_call_output",
          call_id: event.call_id,
          output: JSON.stringify(result),
        },
      })
    );

    // Ask the model to continue with the tool result
    ws.send(
      JSON.stringify({
        type: "response.create",
      })
    );
  }
});
Enter fullscreen mode Exit fullscreen mode

For more complex multi-step agents, OpenAI’s AgentKit provides a higher-level orchestration path.

Test the endpoints with Apidog before you build

Before debugging WebRTC in a half-built app, test each API surface independently. Apidog is useful here because you can validate both REST and WebSocket behavior.

1. Test the ephemeral token endpoint

Create a REST request:

POST https://api.openai.com/v1/realtime/client_secrets
Enter fullscreen mode Exit fullscreen mode

Headers:

Authorization: Bearer YOUR_OPENAI_API_KEY
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode

Body:

{
  "session": {
    "type": "realtime",
    "model": "gpt-realtime-2.1-mini"
  }
}
Enter fullscreen mode Exit fullscreen mode

Expected result:

  • A short-lived token starting with ek_
  • An expiry value
  • Confirmation that your API key and account access work

This is the same kind of smoke test you would run for other OpenAI REST APIs, such as the Responses API.

2. Test the WebSocket event flow

Open a WebSocket connection in Apidog:

wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-mini
Enter fullscreen mode Exit fullscreen mode

Add the auth header:

Authorization: Bearer YOUR_OPENAI_API_KEY
Enter fullscreen mode Exit fullscreen mode

Send these messages manually, one at a time.

First, configure the session:

{
  "type": "session.update",
  "session": {
    "type": "realtime",
    "model": "gpt-realtime-2.1-mini",
    "output_modalities": ["text"],
    "instructions": "You are a concise API support agent."
  }
}
Enter fullscreen mode Exit fullscreen mode

Then add a user message:

{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [
      {
        "type": "input_text",
        "text": "Explain rate limits in one sentence."
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, request a response:

{
  "type": "response.create"
}
Enter fullscreen mode Exit fullscreen mode

Watch the streamed server events in Apidog. This makes the event sequence easier to understand before you implement client-side handlers.

If your team already uses API testing strategies, add these Realtime requests to your normal validation workflow.

When the transport layer works in isolation, app-level bugs are easier to diagnose. You can also Download Apidog to follow along locally.

Keep the bill under control

Audio output is the expensive part. Use these defaults:

  • Tell the model to be brief. Add “Keep answers to one or two sentences” to instructions.
  • Pin a production snapshot. gpt-realtime-mini-2025-12-15 will not drift; gpt-realtime-mini can.
  • Use semantic_vad. It reduces awkward interruptions and wasted partial responses.
  • Cache stable instructions. Cached input is cheaper than fresh text input.
  • Close idle sessions. Do not leave inactive connections open.
  • Avoid unnecessary spoken confirmations. If your UI already shows something, the agent may not need to say it.

Example cost-focused instruction:

You are a concise support agent. Answer in one or two short sentences. Ask one clarifying question only when required. Do not repeat information already visible in the UI.
Enter fullscreen mode Exit fullscreen mode

Common errors and fixes

Error Fix
401 Unauthorized Check your API key. If using WebRTC, mint a fresh ephemeral token per session.
Model not found Use gpt-realtime-2.1-mini, not gpt-realtime-mini-2.1.
No browser audio Attach the remote stream in pc.ontrack and use HTTPS or localhost.
Microphone does not open Confirm browser permissions and getUserMedia requirements.
Model talks over the user Use semantic_vad and confirm the mic track is connected.
WebSocket auth fails Use Authorization: Bearer ... and remove the old OpenAI-Beta header.
No text deltas Confirm output_modalities includes ["text"].
No audio deltas Confirm output_modalities includes ["audio"] and that audio output is configured.

FAQ

Is gpt-realtime-2.1-mini the same as gpt-realtime-mini?

Effectively, yes. gpt-realtime-2.1-mini is the versioned ID. gpt-realtime-mini is the alias that points to the latest snapshot, currently gpt-realtime-mini-2025-12-15.

Use the alias while building. Pin a snapshot for production.

Can I use it for plain transcription?

You can work with transcripts in a Realtime session, but the Realtime API is designed for interactive speech-to-speech conversations. For one-shot transcription, use a dedicated transcription model.

Do I need WebRTC, or is WebSocket enough?

Use WebSocket for server-side pipelines and quick prototypes. Use WebRTC when a browser or mobile app captures and plays audio directly.

Which voice should I pick?

Start with marin or cedar. They are the newest voices, are exclusive to the Realtime API, and are recommended for natural output. The other voices are alloy, ash, ballad, coral, echo, sage, shimmer, and verse.

How is this billed?

Billing is per token and split by modality. For mini:

  • $0.60 / 1M text input tokens
  • $0.30 / 1M cached input tokens
  • $10 / 1M audio input tokens
  • $20 / 1M audio output tokens

Audio output is usually the largest cost driver.

Can it call functions like chat models?

Yes. Realtime uses the same function-calling contract, so a voice agent can look up orders, check inventory, make bookings, or trigger application actions during a conversation.

Where to go next

You now have the implementation path:

  1. Confirm the model ID.
  2. Test access with the text-only WebSocket example.
  3. Switch output_modalities to audio.
  4. Move browser audio to WebRTC with ephemeral tokens.
  5. Configure voice, VAD, and instructions.
  6. Add tools when the conversation needs to take action.
  7. Test REST and WebSocket pieces in Apidog before shipping.

Start with the WebSocket prototype, then move to WebRTC once you need a real microphone and low-latency playback. Pin a snapshot, keep the model concise, and you can build a voice agent that is responsive, testable, and easier to control in production.

Top comments (0)