Skip to content
Docs

OpenAI Responses API WebSocket Mode with AI Gateway

AI Gateway supports WebSocket mode for the Responses API. Instead of opening a new HTTP request for every turn, you open one WebSocket connection and send each turn as a frame. The connection to the model provider stays open between turns, which removes a connection handshake from every turn and cuts per-turn latency in agent loops that make many tool calls.

The wire protocol is OpenAI's Responses WebSocket protocol: you send response.create frames and receive the same response.* events the HTTP streaming API emits.

Open a WebSocket to the same path you'd POST to, and authenticate with your API key:

wss://ai-gateway.vercel.sh/v1/responses Authorization: Bearer $AI_GATEWAY_API_KEY

Send a response.create frame for each turn:

responses-websocket.ts
import WebSocket from 'ws';
 
const ws = new WebSocket('wss://ai-gateway.vercel.sh/v1/responses', {
  headers: { Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}` },
});
 
ws.on('open', () => {
  ws.send(
    JSON.stringify({
      type: 'response.create',
      model: 'openai/gpt-6-astra',
      input: 'Why is the sky blue?',
      store: false,
    }),
  );
});
 
ws.on('message', (data) => {
  const event = JSON.parse(data.toString());
  if (event.type === 'response.output_text.delta') {
    process.stdout.write(event.delta);
  } else if (
    event.type === 'response.completed' ||
    event.type === 'response.failed' ||
    event.type === 'response.incomplete'
  ) {
    console.log();
    ws.close();
  } else if (event.type === 'error') {
    console.error(event.error.message);
    ws.close();
  }
});

The server streams the standard Responses event sequence for each turn: response.created, response.in_progress, output item and content part events, response.output_text.delta chunks, and a terminal response.completed (or response.failed / response.incomplete).

Send the next response.create frame on the same socket after the previous turn completes. Chain turns with previous_response_id:

responses-websocket-turns.ts
ws.send(
  JSON.stringify({
    type: 'response.create',
    model: 'openai/gpt-6-astra',
    input: 'Now explain it to a five-year-old.',
    store: false,
    previous_response_id: firstResponse.id, // from the previous response.completed event
  }),
);

One connection serves one model. Every frame must use the model the connection was opened with; a frame with a different model returns an error and closes the connection with code 1008.

Set store: false to keep the provider from persisting your responses, including under zero data retention. With store: false:

  • previous_response_id continuation works within a connection: the context lives on the open connection, so chained turns work without any server-side storage.
  • A failed turn invalidates the chain. After a response.failed, a previous_response_id pointing at an earlier response in that chain returns previous_response_not_found, even on the same connection. Start a new chain.
  • Continuation does not work across connections. After a reconnect, a previous_response_id from the old connection returns previous_response_not_found. Start a new chain, or resend the conversation context in input.

This is the combination agent loops typically want: persistent context across turns while the connection lives, nothing retained after it closes.

Set service_tier on a response.create frame to request an OpenAI service tier, including ultrafast. AI Gateway bills each turn at the tier OpenAI served. If the model doesn't have pricing for the requested tier, AI Gateway removes the field and the turn runs at the default tier.

responses-websocket-ultrafast.ts
ws.send(
  JSON.stringify({
    type: 'response.create',
    model: 'openai/gpt-6-astra',
    service_tier: 'ultrafast',
    input: 'Why is the sky blue?',
    store: false,
  }),
);

For complete AI SDK, Chat Completions, Responses, and WebSocket examples, see Ultrafast service tiers.

WebSocket mode is available for OpenAI text models, currently GPT-5.4 and later (including the GPT-5.6 series). Requests for any other model return an error frame and close the connection:

{
  "type": "error",
  "status": 400,
  "error": {
    "code": null,
    "message": "Model anthropic/claude-sonnet-4.5 is not available over WebSocket",
    "param": "model",
    "type": "invalid_request_error"
  }
}

Connections are long-lived but not unbounded. Build clients to reconnect and start a new chain:

BehaviorValue
Idle timeout5 minutes without frames, then close 1001. Ping/pong frames reset the timer, so keepalive pings hold an idle connection open.
Maximum connection durationApproximately 13 minutes, then close 1001. Reconnect and start a new chain.
Maximum frame size4 MiB

Close codes:

CodeMeaning
1001Idle timeout or maximum duration reached. Reconnect and continue.
1008Invalid request (for example, an unsupported model or a mid-connection model switch)
1011Upstream provider error
4402Out of credits mid-session. Add credits and reconnect.

Errors arrive as error frames with the same shape as HTTP error responses (status, error.message, error.type), followed by a close. Handle the error event type in your message handler rather than relying only on the close code.

For request parameters, tool calling, structured output, and reasoning configuration, the frames accept the same fields as the HTTP Responses API. WebSocket mode uses the same API fields over a different transport.

Last updated September 8, 2026

Was this helpful?

supported.