Claude SDK Streaming: A Practical Setup Guide
"Claude SDK streaming" usually means one of two things: developers looking for how to call stream=True (or its SDK equivalent) with Anthropic's official Python or TypeScript SDKs, or developers looking for a way to get streaming responses without wiring up the SDK's event handling themselves. This article covers both — the mechanics of streaming with the official SDKs, and a simpler HTTP-based path if that's all you need.
Streaming matters because Claude generates tokens sequentially, and without streaming your app waits for the entire response before showing anything. For chat interfaces, coding assistants, or any UI where perceived latency matters, streaming is not optional — it's the difference between a usable product and a frustrating one.
How Streaming Works in the Official SDKs
Both the Python and TypeScript SDKs expose streaming as an async iterator (or a stream helper object) that yields events as the model generates text. Under the hood this is Server-Sent Events (SSE), but the SDK abstracts the parsing away from you.
Python
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain event loops in Node.js"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final_message = stream.get_final_message()
The stream.text_stream helper filters out everything except plain text deltas, which is what most chat UIs need. If you need full control — tool calls, stop reasons, usage stats — iterate over stream itself instead of text_stream.
TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const stream = client.messages.stream({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Explain event loops in Node.js" }],
});
stream.on("text", (text) => {
process.stdout.write(text);
});
const finalMessage = await stream.finalMessage();
The event-emitter style (stream.on("text", ...)) is convenient in Node backends and works well when you're piping chunks into a WebSocket or an SSE endpoint of your own for the frontend to consume.
Understanding the Event Types
Whether you use the high-level helpers or iterate raw events, it helps to know what's actually coming over the wire:
message_start— the response has begun, includes initial usage datacontent_block_start— a new content block (text or tool_use) is startingcontent_block_delta— incremental text or partial JSON for tool inputscontent_block_stop— a content block finishedmessage_delta— top-level fields changing, likestop_reasonmessage_stop— the stream is complete
If your app uses tool use, you'll see content_block_delta events with partial_json fragments for tool inputs — these need to be concatenated and parsed only once the block closes. Trying to JSON.parse() a partial fragment mid-stream is a common bug.
Common Pitfalls
Forgetting to handle stop_reason: "max_tokens". A stream can end because the model hit its token limit, not because it finished naturally. Check stop_reason on the final message and decide whether to continue the conversation with a follow-up request.
Not handling disconnects. Long-running streams over flaky networks (mobile clients especially) can drop mid-response. Buffer what you've received so you can show a partial answer or offer a retry rather than losing everything.
Blocking the event loop. In Node, writing each streamed chunk synchronously to a slow destination (like a database) will backpressure the stream. Buffer and flush in batches if you're persisting output as it arrives.
Streaming tool calls incorrectly. Tool inputs arrive as JSON fragments across multiple delta events. Concatenate the full string before parsing — don't try to validate or use the JSON until content_block_stop fires for that block.
Rate limits during streaming. A stream can start successfully and then get cut off if you exceed a token-per-minute limit mid-response. Build retry logic that accounts for partial completions, not just full request failures.
A Simpler Path for Teams That Don't Want to Manage Credentials
If your team already has Claude access through a Pro or Team plan and you want an HTTPS API with streaming for internal tools — without provisioning separate Anthropic API keys, managing billing per developer, or building your own key-rotation and usage-tracking layer — SubToAPI turns that access into a standard API with sub_live_... keys.
Streaming through SubToAPI uses the same SSE model, so if you've already built against the official SDK's stream events, migrating the transport layer is mostly a base-URL and auth-header change:
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"}]
}'
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-5",
max_tokens: 1024,
stream: true,
messages: [{ role: "user", content: "Summarize this changelog" }],
}),
});
const reader = response.body.getReader();
// read SSE chunks as they arrive
This is useful when several people on a team need programmatic access with individual keys, usage visibility, and tool use support, without each person setting up separate API billing. See the streaming docs and quickstart for the full event reference, and pricing for plan details.
Questions
Does the Claude SDK support streaming for tool use, not just text? Yes. Streamed responses include content_block_delta events with partial JSON for tool inputs, alongside text deltas. You accumulate the JSON fragments and parse the complete tool call once its content block closes.
What's the difference between stream.text_stream and iterating raw events? text_stream (Python) or the "text" event (TypeScript) gives you plain text deltas only — good for simple chat UIs. Iterating raw events gives you every event type, which you need for tool use, usage tracking, or custom stop-reason handling.
Can I use streaming with SubToAPI the same way as the official Anthropic SDK? Yes, streaming uses the same SSE-based /v1/messages endpoint pattern with "stream": true, so existing client-side stream-parsing code needs minimal changes — mainly the base URL and the sub_live_... API key.