{"slug": "how-to-use-openai-websocket-mode-api", "title": "How to Use OpenAI WebSocket Mode API ?", "summary": "OpenAI offers two WebSocket API modes: a Responses API WebSocket mode for agentic workflows with heavy tool calling, which OpenAI reports is up to 40% faster for workflows with 20 or more tool calls, and a Realtime API for low-latency voice and audio applications. Both replace stateless HTTP requests with a single persistent, bidirectional connection that eliminates repeated connection setup, authentication, and conversation-state resending, with the Responses mode maintaining one previous-response state in a connection-local in-memory cache and remaining compatible with Zero Data Retention and store=false options.", "body_md": "OpenAI offers two WebSocket API modes for different use cases: the **Responses API WebSocket mode** for agentic workflows with heavy tool calling (up to 40% faster for 20+ tool calls), and the **Realtime API** for low-latency voice and audio applications. Both use persistent WebSocket connections instead of stateless HTTP requests, reducing latency by eliminating repeated connection overhead and enabling event-driven, stateful interactions.\n\nEvery HTTP request to OpenAI's API pays a repeated tax: connection setup, authentication, and re-sent conversation state—even when you're continuing the same session. For applications making rapid-fire tool calls or streaming audio, that overhead is pure waste.\n\nOpenAI's WebSocket API eliminates it with a single, persistent, bidirectional connection:\n\n💡 Testing WebSocket connections traditionally required complex debugging tools. Apidog's WebSocket testing interface lets you establish connections, send events, and monitor responses in real time—essential for validating OpenAI WebSocket integrations before production deployment.\n\nThis guide covers both WebSocket modes: the **Responses API** for tool-heavy agent workflows, and the **Realtime API** for audio streaming. You'll learn when to use each, how to implement them, and how to test them effectively.\n\nThe OpenAI WebSocket API is an alternative transport to HTTP for interacting with OpenAI's models. Instead of opening a new connection per API call, you establish one long-lived connection that stays open for the duration of your session.\n\nWebSocket connections start with an HTTP upgrade request, then switch to the WebSocket protocol. For OpenAI, connect to:\n\n`wss://api.openai.com/v1/responses`\n`wss://api.openai.com/v1/realtime?model=gpt-realtime`\nThe `wss://` scheme indicates a secure WebSocket connection (analogous to HTTPS for HTTP).\n\nOpenAI provides two distinct WebSocket modes, each optimized for different use cases.\n\nUse this mode for agentic workflows that make many sequential tool calls: coding assistants, orchestration systems, and autonomous agents that repeatedly call tools to complete complex tasks.\n\n**How it works:**\n\nOn an active connection, the service maintains one previous-response state in a connection-local in-memory cache (the most recent response). To continue a turn, you send only:\n\n`previous_response_id` — a reference to the last response\nThe server reuses the cached state instead of re-processing the entire conversation history.\n\n**Performance benefits:**\n\nFor workflows with 20+ tool calls, OpenAI reports up to 40% faster end-to-end execution compared to HTTP, thanks to:\n\n**Compatibility:** WebSocket mode works with both Zero Data Retention (ZDR) and `store=false` options, making it suitable for privacy-sensitive applications.\n\nUse this mode for low-latency, streaming audio in voice-driven applications—speech-to-speech interactions where the model responds to audio input with audio output and handles interruptions naturally.\n\nThe Realtime API creates a stateful, event-driven session over WebSocket. You stream audio chunks in, and it streams back both transcriptions and generated audio. The connection supports:\n\n**Key features:**\n\nChoosing between WebSocket and HTTP depends on your application's characteristics. Here's when each protocol excels.\n\n**High tool call volume:** If your workflow makes 10+ sequential tool calls, the persistent connection eliminates repeated setup overhead. Each HTTP request requires:\n\nWebSocket does this once, then reuses the connection.\n\n**Latency-sensitive applications:** For real-time voice or chat where every millisecond counts, the persistent connection and streaming capabilities significantly reduce perceived latency.\n\n**Server-initiated updates:** The server can push data to clients without polling. For long-running agent tasks, the server sends progress updates as events occur.\n\n**Simple request-response:** For one-off API calls or workflows with 1–2 tool calls, HTTP is simpler to implement and debug, and infrastructure (load balancers, proxies) handles it well.\n\n**Stateless operations:** If you don't need session state between requests, HTTP's stateless nature is an advantage—no connection management required.\n\n**Infrastructure constraints:** Some environments (serverless functions, certain proxies) don't support long-lived WebSocket connections. HTTP works universally.\n\nBased on OpenAI's documentation and community testing:\n\n| Metric | HTTP | WebSocket (Responses API) | WebSocket (Realtime API) | \n|---|---|---|---|\n| Connection setup | Every request (~100–300ms) | Once (~100–300ms) | Once (~100–300ms) | \n| 20+ tool call workflow | Baseline | ~40% faster | N/A | \n| Voice round-trip latency | N/A (not designed for this) | N/A | <500ms | \n| Memory overhead | Low (stateless) | Medium (cached state) | Medium-High (session state) | \n| Implementation complexity | Low | Medium | Medium-High | \n\nLet's implement a WebSocket connection to the Responses API for an agentic workflow.\n\n`ws` for Node.js or `websocket-client` for Python)\n**Node.js example:**\n\n``` js\nconst WebSocket = require('ws');\n\n// Connect to Responses API WebSocket endpoint\nconst ws = new WebSocket('wss://api.openai.com/v1/responses', {\n  headers: {\n    'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,\n    'OpenAI-Beta': 'responses-api=v1'\n  }\n});\n\nws.on('open', () => {\n  console.log('Connected to OpenAI Responses API');\n\n  // Send initial request\n  const initialMessage = {\n    model: 'gpt-4o',\n    messages: [\n      { role: 'user', content: 'Help me analyze this codebase and suggest improvements.' }\n    ],\n    tools: [\n      {\n        type: 'function',\n        function: {\n          name: 'read_file',\n          description: 'Read contents of a file',\n          parameters: {\n            type: 'object',\n            properties: {\n              path: { type: 'string', description: 'File path to read' }\n            },\n            required: ['path']\n          }\n        }\n      },\n      {\n        type: 'function',\n        function: {\n          name: 'search_code',\n          description: 'Search for code patterns',\n          parameters: {\n            type: 'object',\n            properties: {\n              pattern: { type: 'string', description: 'Regex pattern to search' }\n            },\n            required: ['pattern']\n          }\n        }\n      }\n    ]\n  };\n\n  ws.send(JSON.stringify(initialMessage));\n});\n\nws.on('message', (data) => {\n  const response = JSON.parse(data);\n  console.log('Received:', response);\n\n  // Check if model wants to call tools\n  if (response.choices[0].finish_reason === 'tool_calls') {\n    const toolCalls = response.choices[0].message.tool_calls;\n\n    // Execute tools (simplified)\n    const toolResults = toolCalls.map(call => ({\n      tool_call_id: call.id,\n      output: executeToolLocally(call.function.name, call.function.arguments)\n    }));\n\n    // Continue the conversation with tool results\n    const continuation = {\n      previous_response_id: response.id, // Reference previous response\n      input: toolResults\n    };\n\n    ws.send(JSON.stringify(continuation));\n  }\n});\n\nws.on('error', (error) => {\n  console.error('WebSocket error:', error);\n});\n\nws.on('close', () => {\n  console.log('Connection closed');\n});\n\nfunction executeToolLocally(name, args) {\n  // Your tool execution logic\n  if (name === 'read_file') {\n    const { path } = JSON.parse(args);\n    return fs.readFileSync(path, 'utf-8');\n  }\n  // ... other tools\n}\n```\n\n**Python example:**\n\n``` python\nimport websocket\nimport json\nimport os\n\ndef on_message(ws, message):\n    response = json.loads(message)\n    print(f\"Received: {response}\")\n\n    # Handle tool calls\n    if response['choices'][0]['finish_reason'] == 'tool_calls':\n        tool_calls = response['choices'][0]['message']['tool_calls']\n\n        # Execute tools\n        tool_results = []\n        for call in tool_calls:\n            result = execute_tool(call['function']['name'],\n                                 json.loads(call['function']['arguments']))\n            tool_results.append({\n                'tool_call_id': call['id'],\n                'output': result\n            })\n\n        # Send continuation with only new input + previous_response_id\n        continuation = {\n            'previous_response_id': response['id'],\n            'input': tool_results\n        }\n        ws.send(json.dumps(continuation))\n\ndef on_error(ws, error):\n    print(f\"Error: {error}\")\n\ndef on_close(ws, close_status_code, close_msg):\n    print(\"Connection closed\")\n\ndef on_open(ws):\n    print(\"Connected to OpenAI Responses API\")\n\n    # Send initial request\n    initial_message = {\n        'model': 'gpt-4o',\n        'messages': [\n            {'role': 'user', 'content': 'Analyze this codebase and suggest improvements.'}\n        ],\n        'tools': [\n            {\n                'type': 'function',\n                'function': {\n                    'name': 'read_file',\n                    'description': 'Read file contents',\n                    'parameters': {\n                        'type': 'object',\n                        'properties': {\n                            'path': {'type': 'string'}\n                        },\n                        'required': ['path']\n                    }\n                }\n            }\n        ]\n    }\n    ws.send(json.dumps(initial_message))\n\ndef execute_tool(name, args):\n    if name == 'read_file':\n        with open(args['path'], 'r') as f:\n            return f.read()\n    # ... other tools\n\n# Create WebSocket connection\nws = websocket.WebSocketApp(\n    \"wss://api.openai.com/v1/responses\",\n    header={\n        \"Authorization\": f\"Bearer {os.environ['OPENAI_API_KEY']}\",\n        \"OpenAI-Beta\": \"responses-api=v1\"\n    },\n    on_open=on_open,\n    on_message=on_message,\n    on_error=on_error,\n    on_close=on_close\n)\n\nws.run_forever()\n```\n\n`input` (new data—tool results, user messages, etc.). Don't resend the full `messages` array; the server already has that context.`store: false` in your initial request.\nThe Realtime API enables low-latency voice interactions. Here's how to implement it.\n\n**JavaScript (browser) example:**\n\n``` js\n// Connect to Realtime API\nconst ws = new WebSocket(\n  `wss://api.openai.com/v1/realtime?model=gpt-realtime`,\n  ['realtime', 'openai-insecure-api-key.' + process.env.OPENAI_API_KEY]\n);\n\nws.addEventListener('open', () => {\n  console.log('Connected to Realtime API');\n\n  // Configure session\n  ws.send(JSON.stringify({\n    type: 'session.update',\n    session: {\n      modalities: ['text', 'audio'],\n      voice: 'alloy',\n      input_audio_format: 'pcm16',\n      output_audio_format: 'pcm16',\n      turn_detection: {\n        type: 'server_vad', // or 'semantic_vad' for smarter detection\n        threshold: 0.5,\n        prefix_padding_ms: 300,\n        silence_duration_ms: 500\n      }\n    }\n  }));\n});\n\nws.addEventListener('message', (event) => {\n  const message = JSON.parse(event.data);\n\n  switch (message.type) {\n    case 'session.created':\n      console.log('Session created:', message.session);\n      break;\n\n    case 'conversation.item.created':\n      console.log('New item:', message.item);\n      break;\n\n    case 'response.audio.delta':\n      // Received audio chunk - play it\n      const audioChunk = base64ToArrayBuffer(message.delta);\n      playAudioChunk(audioChunk);\n      break;\n\n    case 'response.audio_transcript.delta':\n      // Received transcript chunk\n      console.log('Transcript:', message.delta);\n      break;\n\n    case 'input_audio_buffer.speech_started':\n      console.log('User started speaking');\n      break;\n\n    case 'input_audio_buffer.speech_stopped':\n      console.log('User stopped speaking');\n      break;\n\n    case 'error':\n      console.error('API error:', message.error);\n      break;\n  }\n});\n\n// Send audio from microphone\nasync function streamMicrophoneToAPI() {\n  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n  const audioContext = new AudioContext({ sampleRate: 24000 });\n  const source = audioContext.createMediaStreamSource(stream);\n  const processor = audioContext.createScriptProcessor(4096, 1, 1);\n\n  processor.onaudioprocess = (e) => {\n    const inputData = e.inputBuffer.getChannelData(0);\n\n    // Convert Float32 to Int16 PCM\n    const pcmData = new Int16Array(inputData.length);\n    for (let i = 0; i < inputData.length; i++) {\n      pcmData[i] = Math.max(-32768, Math.min(32767, inputData[i] * 32768));\n    }\n\n    // Send to API\n    ws.send(JSON.stringify({\n      type: 'input_audio_buffer.append',\n      audio: arrayBufferToBase64(pcmData.buffer)\n    }));\n  };\n\n  source.connect(processor);\n  processor.connect(audioContext.destination);\n}\n\n// Send text input\nfunction sendTextMessage(text) {\n  ws.send(JSON.stringify({\n    type: 'conversation.item.create',\n    item: {\n      type: 'message',\n      role: 'user',\n      content: [\n        { type: 'input_text', text: text }\n      ]\n    }\n  }));\n\n  // Request response generation\n  ws.send(JSON.stringify({\n    type: 'response.create'\n  }));\n}\n\nfunction playAudioChunk(arrayBuffer) {\n  const audioContext = new AudioContext({ sampleRate: 24000 });\n  audioContext.decodeAudioData(arrayBuffer, (buffer) => {\n    const source = audioContext.createBufferSource();\n    source.buffer = buffer;\n    source.connect(audioContext.destination);\n    source.start();\n  });\n}\npython\nimport websocket\nimport json\nimport base64\nimport pyaudio\n\n# Audio configuration\nRATE = 24000\nCHUNK = 4096\nFORMAT = pyaudio.paInt16\nCHANNELS = 1\n\naudio = pyaudio.PyAudio()\n\ndef on_open(ws):\n    print(\"Connected to Realtime API\")\n\n    # Configure session\n    ws.send(json.dumps({\n        'type': 'session.update',\n        'session': {\n            'modalities': ['text', 'audio'],\n            'voice': 'alloy',\n            'input_audio_format': 'pcm16',\n            'output_audio_format': 'pcm16',\n            'turn_detection': {\n                'type': 'server_vad',\n                'threshold': 0.5,\n                'silence_duration_ms': 500\n            }\n        }\n    }))\n\n    # Start streaming microphone\n    stream_microphone(ws)\n\ndef on_message(ws, message):\n    data = json.loads(message)\n\n    if data['type'] == 'response.audio.delta':\n        # Decode and play audio\n        audio_chunk = base64.b64decode(data['delta'])\n        play_audio(audio_chunk)\n\n    elif data['type'] == 'response.audio_transcript.delta':\n        print(f\"Transcript: {data['delta']}\", end='', flush=True)\n\n    elif data['type'] == 'input_audio_buffer.speech_started':\n        print(\"\\n[User speaking...]\")\n\n    elif data['type'] == 'error':\n        print(f\"Error: {data['error']}\")\n\ndef stream_microphone(ws):\n    stream = audio.open(\n        format=FORMAT,\n        channels=CHANNELS,\n        rate=RATE,\n        input=True,\n        frames_per_buffer=CHUNK\n    )\n\n    def audio_thread():\n        while True:\n            audio_data = stream.read(CHUNK)\n            ws.send(json.dumps({\n                'type': 'input_audio_buffer.append',\n                'audio': base64.b64encode(audio_data).decode('utf-8')\n            }))\n\n    import threading\n    threading.Thread(target=audio_thread, daemon=True).start()\n\ndef play_audio(audio_chunk):\n    stream = audio.open(\n        format=FORMAT,\n        channels=CHANNELS,\n        rate=RATE,\n        output=True\n    )\n    stream.write(audio_chunk)\n    stream.stop_stream()\n    stream.close()\n\n# Create WebSocket connection\nws = websocket.WebSocketApp(\n    f\"wss://api.openai.com/v1/realtime?model=gpt-realtime\",\n    header={\n        \"Authorization\": f\"Bearer {os.environ['OPENAI_API_KEY']}\"\n    },\n    on_open=on_open,\n    on_message=on_message\n)\n\nws.run_forever()\n```\n\n**Event types.** The Realtime API uses event-driven communication. Common events:\n\nClient → Server:\n\n`session.update` — configure session parameters`input_audio_buffer.append` — send audio chunks`conversation.item.create` — add text messages`response.create` — request AI response\nServer → Client:\n\n`session.created` — confirms session setup`response.audio.delta` — audio chunk from AI`response.audio_transcript.delta` — transcription of AI audio`input_audio_buffer.speech_started/stopped` — VAD events`error` — error notifications\n**Voice Activity Detection.** Choose between two VAD modes:\n\n`server_vad` — basic voice activity detection based on audio volume and silence duration.`semantic_vad` — smarter detection that understands natural pauses vs. turn completion. Use this for more natural conversations where users might pause mid-thought.\nTesting WebSocket APIs differs from HTTP testing—you need to maintain a connection, send events, and monitor bidirectional message flow. Apidog provides specialized WebSocket testing capabilities.\n\n**Step 1: Create a WebSocket request.** In Apidog, create a new request and select \"WebSocket\" as the protocol. Enter your connection URL:\n\n```\nwss://api.openai.com/v1/responses\n```\n\n**Step 2: Configure headers.** Add authentication headers:\n\n```\nAuthorization: Bearer [REDACTED]\nOpenAI-Beta: responses-api=v1\n```\n\nFor the Realtime API, you can also use URL-based authentication:\n\n```\nwss://api.openai.com/v1/realtime?model=gpt-realtime\n```\n\nwith the API key in the subprotocol header.\n\n**Step 3: Establish the connection.** Click \"Connect.\" Apidog shows:\n\n**Step 4: Send events.** Use Apidog's message composer to send JSON events. For the Responses API:\n\n```\n{\n  \"model\": \"gpt-4o\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"What's the weather in San Francisco?\"\n    }\n  ],\n  \"tools\": [\n    {\n      \"type\": \"function\",\n      \"function\": {\n        \"name\": \"get_weather\",\n        \"description\": \"Get current weather\",\n        \"parameters\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"location\": { \"type\": \"string\" }\n          }\n        }\n      }\n    }\n  ]\n}\n```\n\n**Step 5: Monitor responses.** Apidog displays:\n\nTo test the continuation pattern with `previous_response_id`:\n\n`response.id` in the response.\n\n```\n{\n  \"previous_response_id\": \"resp_abc123\",\n  \"input\": [\n    {\n      \"tool_call_id\": \"call_xyz789\",\n      \"output\": \"{\\\"temperature\\\": 72, \\\"conditions\\\": \\\"sunny\\\"}\"\n    }\n  ]\n}\n```\n\nFor the Realtime API, Apidog lets you:\n\nThis is particularly useful for debugging why your voice assistant might be cutting off users or not detecting speech properly.\n\nStore API keys securely using Apidog's environment variables:\n\n`{{OPENAI_API_KEY}}`\n\nThis lets you switch between development and production keys without editing requests.\n\nHere are practical scenarios where OpenAI's WebSocket modes excel.\n\n**Scenario:** A coding assistant that analyzes codebases, identifies issues, and makes improvements autonomously.\n\n**Why the Responses API WebSocket:**\n\n**Implementation pattern:**\n\n```\n// Initial task\nws.send({ messages: [{ role: 'user', content: 'Audit security vulnerabilities' }], tools: [...] })\n\n// First response: model calls read_file\nws.on('message', (resp1) => {\n  ws.send({ previous_response_id: resp1.id, input: [tool_result_1] })\n})\n\n// Second response: model calls search_code\nws.on('message', (resp2) => {\n  ws.send({ previous_response_id: resp2.id, input: [tool_result_2] })\n})\n\n// Continue for 20+ iterations...\n```\n\n**Scenario:** A phone support bot that handles customer inquiries with natural conversation flow.\n\n**Why the Realtime API WebSocket:**\n\n``` js\n// Stream phone audio to API\nphoneSystem.on('audio', (chunk) => {\n  ws.send({\n    type: 'input_audio_buffer.append',\n    audio: base64Encode(chunk)\n  })\n})\n\n// Play AI responses immediately\nws.on('message', (event) => {\n  if (event.type === 'response.audio.delta') {\n    phoneSystem.playAudio(base64Decode(event.delta))\n  }\n})\n```\n\n**Symptoms:** The WebSocket connection never opens; immediate close event.\n\n**Common causes:**\n\n`Authorization` header`OpenAI-Beta: responses-api=v1`\n`wss://` (not `ws://`) and the endpoint path\n**Solution:** Use Apidog to test the connection with detailed error messages. The request inspector shows exactly which headers are sent, making it easy to spot missing or incorrect API keys.\n\nDebugging code:\n\n``` js\nws.on('error', (error) => {\n  console.error('Connection error:', error);\n});\n\nws.on('close', (code, reason) => {\n  console.log(`Closed with code ${code}: ${reason}`);\n  // Common codes:\n  // 1006: Abnormal closure (often auth issues)\n  // 1008: Policy violation (invalid headers)\n});\n```\n\n**Symptoms:** The WebSocket connection works but isn't faster than HTTP.\n\n**Solution:** Verify you're sending only new input in continuations:\n\n```\n// WRONG - sends full context every time\nws.send({\n  messages: [...allPreviousMessages, newMessage],\n  tools: [...]\n})\n\n// RIGHT - references cached state\nws.send({\n  previous_response_id: lastResponse.id,\n  input: [newMessage]\n})\n```\n\n**Symptoms:** Application memory grows over time with a persistent connection.\n\n**Solution:**\n\n```\n// Clean up event listeners on reconnection\nfunction cleanupAndReconnect(ws) {\n  ws.removeAllListeners();\n  ws.close();\n\n  const newWs = createConnection();\n  return newWs;\n}\n\n// Release audio buffers after playing\nfunction playAndRelease(audioBuffer) {\n  const source = audioContext.createBufferSource();\n  source.buffer = audioBuffer;\n  source.connect(audioContext.destination);\n  source.start();\n\n  source.onended = () => {\n    source.disconnect();\n    // Buffer will be garbage collected\n  };\n}\n\n// Limit message history\nconst messageHistory = [];\nconst MAX_HISTORY = 100;\n\nws.on('message', (data) => {\n  messageHistory.push(data);\n  if (messageHistory.length > MAX_HISTORY) {\n    messageHistory.shift(); // Remove oldest\n  }\n});\n```\n\nOpenAI's WebSocket API modes unlock new possibilities for AI applications. The **Responses API WebSocket mode** delivers up to 40% faster execution for agentic workflows with heavy tool calling, making it ideal for autonomous coding assistants and orchestration systems. The **Realtime API** provides sub-500ms latency for voice applications, enabling natural, interrupt-capable conversations.\n\nChoosing the right mode depends on your use case:\n\nThe persistent, event-driven nature of WebSocket connections requires different testing approaches than HTTP. Test OpenAI's WebSocket APIs with Apidog's real-time WebSocket client—import your API key, establish connections, send events, and monitor responses with detailed logging. Try it free to validate your integrations before production deployment.", "url": "https://wpnews.pro/news/how-to-use-openai-websocket-mode-api", "canonical_source": "https://dev.to/preecha/how-to-use-openai-websocket-mode-api--1e3b", "published_at": "2026-10-16 13:02:43+00:00", "updated_at": "2026-10-16 13:15:32.814621+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "large-language-models", "developer-tools", "ai-products"], "entities": ["OpenAI", "Responses API", "Realtime API", "Apidog", "gpt-realtime"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-to-use-openai-websocket-mode-api", "markdown": "https://wpnews.pro/news/how-to-use-openai-websocket-mode-api.md", "text": "https://wpnews.pro/news/how-to-use-openai-websocket-mode-api.txt", "jsonld": "https://wpnews.pro/news/how-to-use-openai-websocket-mode-api.jsonld"}}