Living Documentation

Your first streamed request

Written by a person. Last read by a person on 2026-09-04, 4 days ago. Its facts were checked by the eval suite on 2026-09-07.

You want one request working before you decide anything else, you are unsure whether to stream or wait for the whole message, or a stream is arriving and you cannot tell which events matter.

One working streamed request, and an explanation of what each part is for.

Why stream at all

A non-streamed request holds one HTTP connection open until the whole response is finished, and returns it in one piece. That is simpler, and it is the right choice for short outputs.

It stops being the right choice for two reasons, and the second is the one that catches people.

The reader is waiting. Nothing appears until everything is ready.

The connection is idle the whole time. Networks drop idle connections. The SDKs validate that a non-streaming request is not expected to exceed a 10-minute timeout, and Anthropic's documentation recommends streaming for anything long-running. A large max_tokens on a non-streamed request is a request to be disconnected.

The request

import anthropic

client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-opus-5",
    max_tokens=64000,
    messages=[{"role": "user", "content": "Explain what a token is, briefly."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Four things in that block are decisions rather than boilerplate.

anthropic.Anthropic() with no arguments. The client resolves credentials from the environment. Passing a key as a literal is how keys reach version control.

.stream(...) as a context manager. The with block closes the stream when you leave it, including when you leave by raising. A stream you abandon without closing holds a connection open.

max_tokens set high. It is a ceiling, not a target, and on a streamed request there is no timeout reason to keep it small. Setting it low to save money does not save money; it truncates the response mid-sentence and you pay again for the retry.

stream.text_stream. This yields only the text. It is the right choice when text is all you want, and the wrong one the moment you care about anything else, which is the next section.

Getting the whole message instead

If you do not need to render tokens as they arrive but you do want the streaming transport, ask the stream for the finished message:

with client.messages.stream(
    model="claude-opus-5",
    max_tokens=64000,
    messages=[{"role": "user", "content": "Explain what a token is, briefly."}],
) as stream:
    message = stream.get_final_message()

print(next(b.text for b in message.content if b.type == "text"))

This is the shape to reach for by default. You get the transport benefits without hand-rolling accumulation, and message is the same object a non-streamed call would have returned.

Note the if b.type == "text". message.content is a list of typed blocks, and assuming the first one is text is a bug that waits until the first response containing anything else.

What a stream is made of

Events, not characters. Each content block announces itself, emits deltas, and stops.

flowchart TD
  A[message_start] --> B[content_block_start]
  B --> C[content_block_delta ...]
  C --> D[content_block_stop]
  D -->|more blocks| B
  D --> E[message_delta]
  E --> F[message_stop]
How a streamed response is structured: the message starts, then each content block starts, emits a series of deltas, and stops, before the message itself stops.

Handling events directly matters when a response can contain more than text:

with client.messages.stream(
    model="claude-opus-5",
    max_tokens=64000,
    messages=[{"role": "user", "content": "Work through this carefully."}],
) as stream:
    for event in stream:
        if event.type == "content_block_delta":
            if event.delta.type == "text_delta":
                print(event.delta.text, end="", flush=True)
            # Other delta types exist. Concatenating all of them is how
            # reasoning ends up spliced into the middle of an answer.

If your first request failed

Before reading anything about retries, work out which half broke. These three take a minute between them and they rule out most of it.

Did the process load the key you think it did? Print its length, never its value. A key that is absent and a key that is wrong produce different errors, and an empty environment variable is much more common than a bad key.

Did you reach the API at all? A connection error, a TLS failure, or an HTML page where JSON should be means you never got there. Nothing about your request is implicated, and no amount of retrying will help until the path works.

Did the API answer with a status? Then you got further than it feels. Your network, your key format and your request shape all worked well enough to be judged, and the status tells you which family the failure is in. Troubleshooting sorts them, and Stuck? Start here is the version organized by what you saw rather than by cause.

What this page has not done

It has not handled a single failure. Every example above assumes the request succeeds, the connection holds, and you are under your rate limit.

That assumption is fine while you are exploring and untenable in anything that runs unattended. Retries and rate-limit backoff is the page that fixes it, and it is the one worth reading properly.