Streaming Responses

Purpose and Scope

Streaming responses let an application consume Claude output as it is generated instead of waiting for the full Message object. In this SDK, the Messages API exposes two practical streaming styles: a higher-level client.messages.stream(...) helper for event-driven applications and a raw client.messages.create({ stream: true, ... }) path for callers that want to iterate the server-sent event stream directly. Both styles are asynchronous and both are designed around the same underlying message stream event model, but they differ in how much accumulation and convenience behavior the SDK provides.

Sources: examples/streaming.ts, examples/raw-streaming.ts, src/resources/messages/index.ts

The official Claude streaming model is server-sent events, often abbreviated SSE. An SSE stream delivers named events and JSON payload fragments over a long-lived HTTP response. At the SDK layer, you usually do not need to parse the wire format yourself: the helper stream yields typed message events, emits useful lifecycle callbacks, and can produce a final accumulated message. Raw streaming is still useful when you want the closest possible mapping to API events, for example writing text deltas to standard output as soon as they arrive or implementing a custom accumulator.

Sources: examples/streaming.ts, examples/raw-streaming.ts, tests/streaming.test.ts

Relevant Source Files

  • examples/streaming.ts demonstrates the high-level client.messages.stream(...) helper, event listeners for contentBlock and message, async iteration over stream events, and finalMessage().
  • examples/raw-streaming.ts demonstrates raw streaming through client.messages.create({ stream: true, ... }) and manual filtering of content_block_delta events with text_delta payloads.
  • tests/streaming.test.ts verifies low-level SSE decoding behavior, including data-only events, event-only records, multiple events, and multi-line data handling.
  • tests/api-resources/MessageStream.test.ts exercises the MessageStream helper behavior, including abort on early loop break, network error handling, final message accumulation, text and tool-use event sequences, and partial JSON parsing for tool input.
  • src/resources/messages/index.ts re-exports the generated Messages resource types, including message stream event types, raw message event types, text deltas, input JSON deltas, stop reasons, and message content block types.

Core Streaming Primitives

The core primitive for most applications is client.messages.stream(params). The example constructs an Anthropic client with the default environment-based API key behavior, starts a stream with messages, model, and max_tokens, then attaches event handlers before iterating the stream. The example uses contentBlock for fully streamed content blocks and message for the fully streamed message, while the for await loop still receives each low-level event. This makes the helper suitable for UIs that need incremental display and also need a final, typed message for storage or follow-up turns.

Sources: examples/streaming.ts, tests/api-resources/MessageStream.test.ts

Raw streaming uses the same Messages API request shape but sets stream: true on client.messages.create(...). The raw example awaits the returned stream and manually inspects each event. It only writes output when the event is a content_block_delta and the nested delta is text_delta, which is the minimal pattern for printing assistant text while ignoring lifecycle events. Use this path when you want explicit control over every event type, when you are building your own state machine, or when you want to keep the SDK’s stream helper behavior out of the critical path.

Sources: examples/raw-streaming.ts, src/resources/messages/index.ts

The generated message exports show the vocabulary that streaming code should expect. The Messages namespace re-exports Message, MessageStreamEvent, RawMessageStreamEvent, RawContentBlockDeltaEvent, RawMessageDeltaEvent, ContentBlockStartEvent, ContentBlockStopEvent, TextDelta, InputJSONDelta, StopReason, and related content block types. Treat these names as the public type surface for narrowing events and deltas in TypeScript. In particular, text streaming and tool input streaming are both delta-oriented, so handlers should branch on both the outer event type and the inner delta type before reading payload fields.

Sources: src/resources/messages/index.ts

Execution Flow

A typical high-level streaming flow has four phases. First, create an Anthropic client and call client.messages.stream(...) with the same fields you would pass to a non-streaming message request. Second, optionally register helper events such as contentBlock or message to react when accumulated units are complete. Third, use for await (const event of stream) to process incremental events in arrival order. Fourth, after iteration completes, call finalMessage() when you need the fully accumulated assistant message object. The repository example logs all events and then logs the final message, making the relationship between incremental and accumulated views explicit.

Sources: examples/streaming.ts

A typical raw flow is simpler but more manual. Call client.messages.create(...) with stream: true, then iterate events and write only the fragments you care about. The raw example checks for content_block_delta and text_delta, then writes event.delta.text to process.stdout. This pattern avoids waiting for a newline or a completed content block, which is useful for terminal output and interactive user interfaces. Because raw event handling exposes lifecycle and delta details directly, production code should include an exhaustive event strategy rather than assuming every event carries text.

Sources: examples/raw-streaming.ts, tests/api-resources/MessageStream.test.ts

Deltas, Accumulation, and Tool Use

Streaming is not only for plain text. The MessageStream tests define expected sequences for both a basic text response and a tool-use response. The basic response proceeds through message_start, content_block_start, several content_block_delta events, content_block_stop, message_delta, and message_stop. The tool-use response includes multiple content blocks and additional deltas before the final message_delta and message_stop. This test shape is important because applications that support tools should not treat a stream as a single text buffer; they need to accumulate per content block and preserve the final message structure.

Sources: tests/api-resources/MessageStream.test.ts, src/resources/messages/index.ts

Tool input deltas can require partial JSON handling. The MessageStream test imports the SDK’s vendored partial JSON parser and mocks partialParse so the test can count parser calls while exercising tool-use streaming behavior. That is a signal that tool input may arrive incrementally and should be accumulated cautiously. Claude’s fine-grained tool streaming documentation also warns that tool input fragments may be partial or invalid JSON until the stream completes. In SDK code, prefer type narrowing on InputJSONDelta and treat intermediate values as previews rather than validated final tool arguments.

Sources: tests/api-resources/MessageStream.test.ts, src/resources/messages/index.ts

Low-Level SSE Decoding and Failure Behavior

The lower-level streaming tests validate the SSE parser that powers streamed responses. They cover a basic event with event: and data: lines, data without an explicit event name, an event without data, multiple events in one response, and events with multiple data lines. These cases matter because real SSE responses are line-oriented, and the parser must preserve event boundaries while combining data lines correctly. If you are debugging unusual streaming behavior, separate wire decoding questions from message accumulation questions: _iterSSEMessages concerns SSE records, while MessageStream concerns Messages API semantics.

Sources: tests/streaming.test.ts, tests/api-resources/MessageStream.test.ts

The helper stream also has observable cancellation and error semantics. One test breaks out of a for await loop after seeing a text delta containing part of the response, then expects stream.done() to reject with APIUserAbortError and stream.aborted to be true. Another test area covers network errors through a mocked fetch. For application code, this means early loop exit is not just a passive stop; it aborts the stream. Use break intentionally, handle done() or finalMessage() rejection paths, and distinguish user cancellation from transport failures in your logs and UI.

Sources: tests/api-resources/MessageStream.test.ts

Compact API Reference

NeedSDK surfaceNotes
High-level message streamingclient.messages.stream(params)Returns a helper stream that is async iterable and supports helper events shown in the example.
Raw SSE-style message streamingclient.messages.create({ stream: true, ...params })Returns an async iterable of stream events for manual filtering and accumulation.
Complete accumulated resultstream.finalMessage()Used after high-level stream iteration to retrieve the final Message.
Completion or cancellation waitstream.done()Tests show it rejects with APIUserAbortError after user abort through early loop break.
Event typingMessageStreamEvent, RawMessageStreamEvent, TextDelta, InputJSONDeltaRe-exported from the generated Messages namespace for TypeScript narrowing.
Common text delta guardevent.type === 'content_block_delta' && event.delta.type === 'text_delta'Used by the raw streaming example before reading event.delta.text.

Testing Signals and Next Steps

The repository’s examples and tests give a practical confidence model for streaming integrations. Start with examples/streaming.ts when you want the SDK to accumulate content blocks and final messages. Start with examples/raw-streaming.ts when you want to see exactly how to filter text deltas from raw events. When behavior is surprising, compare it to the expected event sequences in tests/api-resources/MessageStream.test.ts and the SSE edge cases in tests/streaming.test.ts. For related topics, read the Messages API page for non-streaming request shape, Tool Use Overview for tool-call semantics, and Fine-Grained Tool Streaming for latency-sensitive tool input handling.

Sources: examples/streaming.ts, examples/raw-streaming.ts, tests/streaming.test.ts, tests/api-resources/MessageStream.test.ts