●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Claude API Streaming × Real-Time Chat UI: Production Implementation Guide
A practical guide to running Claude API streaming with Server-Sent Events in Next.js App Router at production grade, with measured latency, recovery patterns, and Cloudflare Workers edge-relay details from real indie operation
Claude API's streaming capabilities transform the traditional "request → wait for full response → display" paradigm into a dynamic "request → receive partial responses in real-time → update UI" experience. For chat applications, watching the AI respond as it "thinks" dramatically boosts user engagement and trust.
However, streaming introduces complexity. Mid-stream disconnections, timeouts, Tool Use state management, rate limit monitoring, and other non-functional requirements multiply rapidly.
I have been shipping iOS and Android apps as an indie developer since 2014, and the portfolio has accumulated more than 50 million downloads. Across that span I have implemented many flavors of real-time network experiences, and Claude API streaming sits on that continuum — with one twist: the AI's "thinking" can pause mid-flight, and a Tool Use can interrupt the flow. This guide attacks those rough edges from a real operations point of view, not a textbook one.
How Claude API Streaming Works
Server-Sent Events (SSE) Protocol
Claude API streaming uses the HTTP standard Server-Sent Events (SSE) protocol. SSE maintains an HTTP connection while continuously streaming multiple events from server to client.
SSE characteristics:
Text-based HTTP/1.1 standard protocol
Browser's EventSource API handles automatic reconnection with heartbeat
Simpler than WebSocket (but unidirectional only)
Excellent compatibility with firewalls, proxies, and CDNs
Claude API Event Stream Structure
A Claude streaming response consists of this event sequence:
message_stop: Complete stream termination (safe to close connection)
Critical Implementation Details
Never assume event order: Implementations relying on arrival order are fragile. Always reconstruct content based on the index field.
Tool Use phases: When Tool Use appears during streaming, the flow is: Tool Use received completely → tool execution → continue streaming. This differs from text-only responses.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Measured numbers from indie operation: TTFB P50/P95, connection-hold rate, cache hit ratio, and stop-reason distribution that actually drive cost decisions
✦A connection-loss recovery design that uses conversation-history replay instead of resume-from-offset, surviving mid-Tool-Use disconnects without confusing the user
✦Cloudflare Workers × EventSource buffering pitfalls and the exact response headers, TransformStream tick, and heartbeat cadence that keep tokens flowing smoothly
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
What follows is not in the Anthropic documentation. These are notes from running Claude API streaming in a real indie production setting, where most of the genuine failure modes hide.
Treat Recovery as History Replay, Not Resume-From-Offset
Thinking "let me resume from where the stream cut off" pushes you against the protocol. Claude API requires the full conversation history in each request, so a true resume is impossible. The approach that survives production looks like this:
If the output broke off under roughly 50 tokens, the client silently discards the partial message and re-issues the request. The user never sees the half-rendered output.
If more than 50 tokens were already on screen, we insert the partial output into history as an assistant turn, append a one-line system note like "the previous reply was interrupted, please continue from where you left off," and stream again.
Since I moved to this rule, the perceived disconnect rate in my chat prototypes dropped from roughly 3 out of 10 sessions to under 1 out of 10. Users almost never notice that anything happened.
Always Build a Cancel Path for Step 2+ of a Tool Use Chain
A single Tool Use is rare in production. Real flows run two to three Tool Uses chained together. The moment a user thinks "this is going the wrong direction," you need a clean cancel path — not an "abandon the browser tab" path.
The skeleton: pass an AbortController to messages.stream() inside your server Route Handler, and expose a DELETE endpoint from the client that calls controller.abort() on the server. Discard the in-flight Tool Use result, keep the content_block_delta text that already arrived, and write that text into history as the assistant's partial reply. Without this, users come away thinking the stop button is decorative — and they do not come back.
Monitor P95 Latency, Not P50
The perceived quality of streaming is determined by time to first byte (TTFB) — the silent gap before the first token shows up. In my wallpaper and relaxation apps (AdMob-driven mostly), users complain about that opening silence far more than about mid-stream slowness.
From the Cloudflare Workers + Claude API edge-relay setup I actually run, the typical numbers are:
TTFB P50: about 380–450 ms (same region, ~600-token system prompt)
TTFB P95: about 900–1,200 ms (including one retry)
Connection hold rate (streams over 90 s): 98.7%
Auto-recovery success rate using the replay pattern above: 94%
If you only watch P50, the 5% of users who sit through that long opening silence quietly leave. I tag every session with TTFB above 1.2 s as a red row in the log and bucket the cause across Anthropic status, my own network, and prompt size.
Hold Costs Down With Two Metrics: Cache Hit Rate and Stop-Reason Distribution
The frightening month is the one where the bill is 2–3× what you projected. Two numbers keep me out of that month:
Cache hit rate for the prompt-cache portion over the trailing 7 days. If it sits under 50% for long, restructure the static prefix of your system prompt — usually some piece is changing per request that does not need to.
Stop-reason distribution across end_turn / max_tokens / tool_use / stop_sequence from message_delta. Once max_tokens cutoffs cross 15%, complaints about "it stops in the middle" start arriving. Either raise max_tokens or coach the response template to be shorter by structure.
The Anthropic Usage dashboard only gives you a coarse monthly figure, so I write both numbers from wrangler tail logs into Workers Analytics Engine for a daily rollup. Daily granularity is what lets you react before the invoice does.
Avoid Buffer Stalls in Cloudflare Workers × EventSource
When you relay Claude streams through a Worker into a browser EventSource, you can hit a pattern where chunks land in 4 KB bursts instead of dribbling in. That is the Workers response stream buffering internally, and the user-visible result is "the answer suddenly appears" — defeating the point of streaming entirely.
These three changes have kept that stable for me:
Add Cache-Control: no-cache, no-transformandX-Accel-Buffering: no to every streaming response
Wrap the relay in a TransformStream and insert one await new Promise((r) => setTimeout(r, 0)) right after each controller.enqueue() to flush the microtask queue
Send a heartbeat event: ping\ndata: {}\n\n every 5–10 tokens, both as a flush hint and as proxy idle-timeout insurance
This was the last small stall I cleared when I replaced the on-device dictionary in one of my wallpaper apps with a Claude-backed lookup. I have been independently coding since I first taught myself to program over the early internet back in 1997, and these protocol-seam fixes are still the part I enjoy most.
When to Stop Using Streaming Altogether
A note on the inverse direction: there are screens where you should drop streaming and go back to a single response.
The response is reliably under 300 tokens (title generation, short summaries). Streaming gains nothing perceivable and only pays the latency penalty.
The backend has to run post-processing on the full body (JSON Schema validation, normalization). Non-streaming plus a deterministic post-step is simpler and more reliable.
The output gets cached and reused (FAQ bot, fixed canned answers). Generate once into KV, serve flat.
"Stream everything" is more expensive to build and to debug than people expect. Reserve streaming for screens where the user hates silence — that has been the single rule that kept my long-running apps cheap to operate.
Production Checklist
When I push a Claude API streaming × chat UI into real operation, this is what I verify on the way out the door:
Protocol: SSE structure understood, index-based reconstruction, every message_delta.stop_reason handled
Connection: 50-token recovery threshold wired in, AbortController path for cancel, heartbeat present
Tool Use: cancel possible at every step in a chain, partial results preserved in history
Observability: TTFB P95, connection-hold rate, cache hit ratio, and stop-reason distribution all visible at daily granularity
Edge relay: X-Accel-Buffering: no, TransformStream tick flush, heartbeat cadence set
With those in place, you can ship the "watch the AI think live" experience without losing nights to alert pages.
A reasonable next step is to record your own app's TTFB P95 for one week. The moment "it is slower than I thought, some of the time" becomes a number on a chart, the rest of this guide stops being abstract and becomes a checklist with your name on it.
Thanks for reading. If you are running indie operations on a similar setup, I hope a couple of these notes save you a late-night incident.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.