← Blog

Claude API WebSocket Streaming Implementation Guide

2026-09-25 · 5 min read · SubToAPI Team

If you're searching for a "Claude API WebSocket" endpoint, the short answer is: it doesn't exist. Anthropic's Claude API streams tokens over HTTP using Server-Sent Events (SSE), not WebSockets. There is no wss:// endpoint to connect to, no socket handshake, no bidirectional protocol built into the API itself.

What most developers actually want when they search this phrase is a way to get Claude's streamed output into a system that's already built around WebSockets — a real-time chat app, a game backend, a mobile client using socket.io, or infrastructure that multiplexes many concurrent conversations over a single connection. That's a solvable, common problem: you build a thin WebSocket layer on your own server that consumes the SSE stream from Claude and re-broadcasts it to your WebSocket clients. This article walks through that implementation.

Why Claude Doesn't Expose WebSockets Natively

SSE is a better fit than WebSockets for one-directional, request-triggered streaming like an LLM completion:

WebSockets add value when you need full-duplex communication or want to fan a single upstream stream out to many downstream consumers (e.g., broadcasting one generation to multiple viewers, or maintaining persistent connections for a real-time multiplayer app). That's an application-layer concern, not something the model API needs to solve for you.

The Architecture: SSE In, WebSocket Out

The pattern is straightforward:

  1. Your backend opens an SSE connection to Claude (or a gateway like SubToAPI) using standard streaming.
  2. As chunks arrive, you parse the SSE events and forward the relevant deltas to connected WebSocket clients.
  3. You manage connection lifecycle, backpressure, and error handling on your own server rather than relying on the model API for it.

This keeps your API key server-side, lets you fan out one generation to multiple clients, and gives you full control over framing and reconnection logic.

Server: Bridging SSE to WebSocket in Node.js

import { WebSocketServer } from 'ws';
import http from 'http';

const server = http.createServer();
const wss = new WebSocketServer({ server });

wss.on('connection', (ws) => {
  ws.on('message', async (raw) => {
    const { prompt } = JSON.parse(raw.toString());

    const response = 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',
        max_tokens: 1024,
        stream: true,
        messages: [{ role: 'user', content: prompt }]
      })
    });

    const reader = response.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 lines = buffer.split('\n\n');
      buffer = lines.pop();

      for (const line of lines) {
        const dataLine = line.split('\n').find(l => l.startsWith('data:'));
        if (!dataLine) continue;

        const payload = dataLine.replace('data:', '').trim();
        if (payload === '[DONE]') {
          ws.send(JSON.stringify({ type: 'done' }));
          continue;
        }

        const event = JSON.parse(payload);
        if (event.type === 'content_block_delta') {
          ws.send(JSON.stringify({
            type: 'delta',
            text: event.delta?.text ?? ''
          }));
        }
      }
    }
  });
});

server.listen(3001);

This gives your frontend a single persistent socket where it sends a prompt and receives incremental delta messages, regardless of how the upstream API actually streams data. If you swap providers or gateways later, only this bridge changes — your client code stays the same.

Client: Consuming the WebSocket

const socket = new WebSocket('wss://your-server.example/stream');

socket.onopen = () => {
  socket.send(JSON.stringify({ prompt: 'Summarize this in 3 bullet points.' }));
};

socket.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === 'delta') {
    appendToUI(msg.text);
  } else if (msg.type === 'done') {
    finalizeUI();
  }
};

Handling Reconnection and Backpressure

A few things you need to handle yourself once you're running this bridge in production:

Where SubToAPI Fits

If you're already sending requests through SubToAPI, the SSE stream your bridge consumes is the same messages endpoint documented at /docs/messages and /docs/streaming — application keys (sub_live_...), usage metadata, and tool calls all work the same way whether you're consuming the stream directly in a browser or relaying it through your own WebSocket layer as shown above. This is useful if your team wants centralized key management and usage visibility while still building custom real-time infrastructure on top. Get started at /signup or check /docs/quickstart for the basic request format before wiring up the bridge.

questions

Does Claude's API support WebSockets directly? No. Claude streams responses over HTTP using Server-Sent Events. To get WebSocket behavior, you build a bridge server that consumes the SSE stream and forwards messages to WebSocket clients.

Why would I want WebSockets if SSE already streams tokens? WebSockets make sense when you need bidirectional communication, want to fan one generation out to multiple connected clients, or your existing infrastructure (mobile apps, real-time backends) is already built around persistent sockets rather than HTTP streaming.

What's the biggest pitfall when building this bridge myself? Backpressure and reconnection. Slow WebSocket clients can buffer memory indefinitely if you don't monitor bufferedAmount, and unlike EventSource, raw WebSockets require you to implement your own reconnect and replay logic.

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 →