← Blog

Claude API Streaming: Server-Sent Events Example

2026-11-06 · 4 min read · SubToAPI Team

If you're searching for a Claude API streaming server-sent events example, you want to see tokens arrive incrementally instead of waiting for a full response. Claude's streaming mode uses the SSE (Server-Sent Events) protocol: the server keeps the HTTP connection open and pushes small data: chunks as the model generates text, instead of returning one big JSON blob at the end.

This matters for any chat UI, CLI tool, or voice assistant where perceived latency matters more than total completion time. A user watching words appear feels like the app is faster, even if the total generation time is identical. Below is a working example of how the request, response format, and client-side parsing actually look.

How Claude API Streaming Works

When you set "stream": true in your request, the API responds with Content-Type: text/event-stream and sends a sequence of named events rather than a single payload. Each event looks like this:

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

You'll typically see events in this order:

Your client needs to parse each data: line as JSON, check the type field, and append delta.text to whatever buffer you're rendering.

A curl Example

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet-20241022",
    "max_tokens": 512,
    "stream": true,
    "messages": [
      {"role": "user", "content": "Write a haiku about streaming APIs"}
    ]
  }'

You'll see raw SSE lines print to your terminal as they arrive. This is useful for debugging, but production code needs a proper SSE parser.

A JavaScript Streaming Client

Here's a minimal browser/Node example using fetch and a ReadableStream reader:

async function streamMessage(prompt) {
  const response = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": process.env.ANTHROPIC_API_KEY,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 512,
      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");
    buffer = lines.pop(); // keep incomplete line for next chunk

    for (const line of lines) {
      if (!line.startsWith("data:")) continue;
      const payload = line.replace("data:", "").trim();
      if (!payload) continue;

      const event = JSON.parse(payload);
      if (event.type === "content_block_delta") {
        process.stdout.write(event.delta.text); // or append to UI state
      }
    }
  }
}

The key detail most examples skip: SSE chunks don't always align with line boundaries. You must buffer partial lines and only parse complete ones — otherwise you'll get JSON.parse errors on truncated payloads.

Common Pitfalls

Streaming Through a Proxy or Gateway

If you're routing Claude requests through your own backend (for key management, logging, or rate limiting), you need to make sure your proxy forwards the stream chunk-by-chunk rather than collecting the full response first. This is a common mistake with Express middleware or Nginx configs that buffer by default — you have to explicitly disable buffering for SSE routes.

SubToAPI handles this for you if you'd rather not build and maintain a streaming proxy. It exposes your Claude access as a hosted HTTPS API with sub_live_... application keys, and streaming works the same SSE way as the raw Anthropic endpoint — the parsing logic above is portable. You get usage metadata per request and team seats without writing your own passthrough layer. See the streaming docs and quickstart for the exact request shape, or check messages for the non-streaming equivalent. Plans start at pricing with a free trial at signup.

questions

Does Claude API streaming use WebSockets or SSE? Server-Sent Events over a standard HTTP connection, not WebSockets. You send one POST request with stream: true and read the response body as a continuous stream of data: lines.

Why does my stream hang or buffer until the end? Usually an intermediary (reverse proxy, serverless runtime, or Node framework) is buffering the response instead of flushing chunks immediately. Disable response buffering for that route and confirm with curl -N locally.

Can I cancel a stream mid-generation? Yes — closing the underlying connection (aborting the fetch request or the HTTP socket) stops the server from sending further tokens, though you may still be billed for tokens already generated up to that point.

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 →