← Blog

Claude API WebSocket Streaming: Implementation Guide

2026-11-03 · 5 min read · SubToAPI Team

If you searched for "Claude API WebSocket streaming," here's the fact that saves you hours of debugging: Anthropic's Claude API does not expose a WebSocket endpoint. Streaming is implemented over HTTP using Server-Sent Events (SSE), a one-way streaming protocol built on top of a regular HTTP response. There is no wss:// URL to connect to, no socket handshake, and no bidirectional channel on Anthropic's side.

That doesn't mean you can't build a WebSocket-based architecture around Claude — many production apps do, especially when a browser client needs a persistent connection to a backend. This guide covers both halves of the problem: how Claude's actual streaming protocol (SSE) works, and how to implement a WebSocket layer in front of it if your frontend needs one.

Why Claude streaming is SSE, not WebSocket

SSE is the right fit for LLM output because the data flow is one-directional: the client sends one request, the server pushes a sequence of events back, and the connection closes when the response is done. WebSockets exist to support bidirectional, long-lived sessions where either side can send messages at any time — useful for chat apps and multiplayer tools, but unnecessary overhead for a single request/response generation.

When you call Claude's Messages endpoint with "stream": true, the server keeps the HTTP connection open and sends a series of event:/data: chunks as tokens are generated:

event: message_start
data: {"type":"message_start","message":{"id":"msg_01...","role":"assistant"}}

event: content_block_delta
data: {"type":"content_block_delta","delta":{"type":"text_delta","text":"Hello"}}

event: content_block_delta
data: {"type":"content_block_delta","delta":{"type":"text_delta","text":" there"}}

event: message_stop
data: {"type":"message_stop"}

Your job as the implementer is to parse this event stream and forward the tokens to wherever they need to go — a terminal, a web UI, or a WebSocket connection to a browser.

Implementing SSE consumption with curl

A quick way to see raw streaming output without writing code:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "stream": true,
    "max_tokens": 512,
    "messages": [{"role": "user", "content": "Explain SSE in two sentences."}]
  }'

You'll see the raw event chunks print to your terminal as they arrive. If you're using SubToAPI, this works the same way whether you're calling Anthropic directly or through api.subtoapi.app — the request/response shape and streaming format are preserved, since SubToAPI is a thin, compatible layer. Full field-by-field details are in the streaming docs.

Implementing SSE consumption in Node.js

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "claude-sonnet-4",
    stream: true,
    max_tokens: 512,
    messages: [{ role: "user", content: "Stream me a short poem." }],
  }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  const events = buffer.split("\n\n");
  buffer = events.pop(); // keep incomplete chunk for next loop

  for (const chunk of events) {
    const line = chunk.split("\n").find((l) => l.startsWith("data:"));
    if (!line) continue;
    const payload = JSON.parse(line.slice(5).trim());
    if (payload.type === "content_block_delta") {
      process.stdout.write(payload.delta.text);
    }
  }
}

This is the core pattern: read the stream in chunks, split on double newlines to separate SSE events, parse the data: field, and act on the delta type.

Wrapping SSE in a WebSocket layer

If your use case is a browser client that needs a persistent connection — for example, a chat UI where the server pushes updates proactively, not just in response to a request — you can build a small relay:

  1. Client opens a WebSocket connection to your backend.
  2. Your backend receives a message over that socket and makes an SSE streaming call to Claude (directly or via SubToAPI).
  3. As SSE chunks arrive, your backend forwards each token to the client over the open WebSocket.
  4. On message_stop, you send a completion signal over the socket but keep the connection alive for the next turn.
wss.on("connection", (socket) => {
  socket.on("message", async (raw) => {
    const { prompt } = JSON.parse(raw);
    const res = await fetch("https://api.subtoapi.app/v1/messages", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model: "claude-sonnet-4",
        stream: true,
        max_tokens: 1024,
        messages: [{ role: "user", content: prompt }],
      }),
    });

    for await (const token of parseSSE(res.body)) {
      socket.send(JSON.stringify({ type: "delta", text: token }));
    }
    socket.send(JSON.stringify({ type: "done" }));
  });
});

This gives you the responsiveness of WebSockets at the UI layer while Claude itself is consumed over SSE. It also centralizes your API key on the server — never expose it to a browser client.

Handling tool use and streaming together

Streaming gets more complex when the model calls a tool mid-response. Tool inputs arrive as partial JSON deltas inside content_block_delta events with type: "input_json_delta", and you need to accumulate them until the block closes before you can execute the tool. If your app uses tools, read the tool use docs alongside the streaming docs before building the parser — the event sequence changes when a tool call is involved.

Reconnection and error handling

SSE connections can drop mid-stream from network issues or timeouts. Design for it:

If you're evaluating providers rather than committing to raw API calls, check pricing and the quickstart — SubToAPI issues sub_live_... application keys, handles streaming the same way described above, and adds usage metadata per request without you building that tracking yourself.

questions

Does Claude's API support native WebSocket connections? No. Anthropic's Messages API streams responses using Server-Sent Events (SSE) over a standard HTTP connection, not WebSockets. If you need a WebSocket interface, you build it yourself as a relay in front of the SSE stream.

Why would I want WebSockets if Claude already streams via SSE? WebSockets are useful when your frontend needs a persistent, bidirectional connection — for example, pushing server-initiated updates or supporting multiple concurrent requests on one socket. SSE is sufficient for simple one-request, one-response streaming UIs.

Can I use SubToAPI for streaming responses? Yes. SubToAPI's /v1/messages endpoint supports "stream": true and streams the same SSE event format described in this guide. See the streaming docs and signup to get a key.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →