← Blog

Claude API Streaming Response Implementation Guide

2026-10-08 · 5 min read · SubToAPI Team

Streaming lets your application display Claude's response token by token instead of waiting for the entire completion to finish. This guide shows exactly how to implement it: setting the right request parameters, parsing server-sent events (SSE), handling partial JSON for tool use, and avoiding the most common bugs that break streaming in production.

If you're here because your UI freezes during long responses, or because you're building a chat interface and want that familiar "typing" effect, streaming is the fix. It's not complicated once you understand the event format Claude sends back — the hard part is usually on the client side: buffering chunks correctly, handling disconnects, and not blocking your event loop.

How Claude API Streaming Works

Instead of returning one JSON blob after generation completes, a streaming request keeps the HTTP connection open and sends a sequence of events as text/event-stream. Each event is a small JSON payload prefixed with event: and data: lines. The client reads these incrementally and appends text as it arrives.

The event types you'll see in a typical streaming response:

You enable streaming by setting "stream": true in your request body. Everything else about the request — model, messages, system prompt, max_tokens — stays the same.

Basic Implementation with curl

For debugging, curl is the fastest way to see raw SSE output:

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-sonnet-4-5",
    "max_tokens": 1024,
    "stream": true,
    "messages": [{"role": "user", "content": "Explain event loops in Node.js"}]
  }'

You'll see a stream of event: / data: pairs scroll by in the terminal. This is useful for confirming your prompt and parameters work before writing client code.

Implementing Streaming in JavaScript

In a browser or Node.js environment, you typically use fetch with a readable stream reader rather than the EventSource API, since EventSource doesn't support custom headers or POST bodies.

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-sonnet-4-5",
      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");
    buffer = lines.pop(); // keep incomplete line for next chunk

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

      const event = JSON.parse(payload);
      if (event.type === "content_block_delta") {
        process.stdout.write(event.delta.text ?? "");
      }
    }
  }
}

The key detail most implementations get wrong: SSE chunks don't align with JSON message boundaries. A single read() call might return half an event, or several events at once. You must buffer incomplete lines and only parse complete ones — that's what the buffer.split("\n") / buffer.pop() pattern handles above.

Handling Tool Use in Streams

When Claude calls a tool mid-stream, the JSON arguments arrive as partial fragments across multiple content_block_delta events with type: "input_json_delta". You need to accumulate these fragments into a string and only JSON.parse() once the block closes (content_block_stop). Parsing partial JSON on every delta will throw errors — wait for the full accumulated string.

let toolInputJson = "";

if (event.type === "content_block_delta" && event.delta.type === "input_json_delta") {
  toolInputJson += event.delta.partial_json;
}

if (event.type === "content_block_stop") {
  const toolInput = JSON.parse(toolInputJson);
  // execute tool with toolInput
}

Error Handling and Reconnection

Streaming connections can drop mid-response due to network issues or timeouts. A production implementation needs to:

Streaming via SubToAPI

If you're already calling the Claude API through your own backend, SubToAPI gives you the same streaming behavior over a stable HTTPS endpoint with an sub_live_... application key, so you don't have to manage provider-specific SDKs or rotate raw provider credentials across services. The request shape is nearly identical:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "stream": true,
    "messages": [{"role": "user", "content": "Summarize this changelog"}]
  }'

This is useful if you're issuing separate API keys per app or per team seat and want usage metadata on each streamed request without building that tracking yourself. See the streaming docs and messages reference for the full event schema, or the quickstart to get a key from signup.

Common Pitfalls

Questions

Does streaming cost more than non-streaming requests? No. Token usage and pricing are identical — streaming only changes how the response is delivered, not how it's billed.

Can I use the EventSource browser API instead of fetch? Not directly, because EventSource only supports GET requests without custom headers, and Claude API calls require POST with an API key header. Use fetch with a stream reader instead.

What happens if my client disconnects mid-stream? The server-side generation typically continues until completion or token limit, but any tokens generated after disconnect are lost to that client. Your application should detect the disconnect and offer the user a retry rather than attempting to resume the same stream.

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 →