7.0 What This Teaches
Streaming lets you display model output as it is generated instead of waiting
for the complete response. This tutorial covers:
- Why streaming improves user experience for long responses
- The
client.messages.stream() context manager
- Iterating over
stream.text_stream to get chunks as they arrive
- Using
stream.get_final_message() to retrieve usage stats after streaming
- When streaming adds unnecessary complexity
7.1 Why Streaming
Without streaming, messages.create() blocks until the model finishes
generating the entire response. A 500-token reply might take 3-5 seconds. During
that time the user sees nothing.
With streaming, the model sends tokens as it generates them. The first token
typically arrives within a few hundred milliseconds. For a user watching a
terminal or a UI, text appearing progressively feels much faster than a
multi-second blank wait followed by a sudden wall of text.
The tradeoff: streaming requires a context manager and a loop instead of a
single function call. For scripts that process output programmatically and
never display it to a user, the extra complexity is not worth it.
7.2 The Streaming API
The SDK provides streaming through client.messages.stream(), used
as a Python context manager. Inside the with block, the
stream object gives you access to text_stream - an
iterator that yields each text chunk as it arrives from the API.
with client.messages.stream(...) as stream:
for text in stream.text_stream:
# text is one chunk - usually a word or a few characters
print(text, end="", flush=True)
The parameters passed to stream() are the same as for
messages.create(): model, max_tokens,
messages, and optionally system.
7.3 Basic Streaming Example
# stream_hello.py - print tokens as they arrive.
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=256,
messages=[{"role": "user", "content": "Write a Python quicksort function."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print() # newline after stream ends
Run this script and watch the function appear character by character.
Two details in the print call matter:
-
end="" - suppresses Python's default newline after each print,
so all chunks print on the same line flow instead of each on its own line.
-
flush=True - forces Python to send each chunk to the terminal
immediately instead of buffering it. Without this, output may appear all at
once at the end, defeating the purpose of streaming.
7.4 Getting the Final Message Object
After the loop finishes, stream.get_final_message() returns the
complete Message object with all fields populated, including
usage. Call it after the loop, not inside it.
# stream_with_usage.py - stream output then read usage stats.
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=256,
messages=[{"role": "user", "content": "Write a Python quicksort function."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# get_final_message() is called after the loop, inside the with block
final = stream.get_final_message()
print() # newline after stream ends
print(f"\nInput tokens : {final.usage.input_tokens}")
print(f"Output tokens: {final.usage.output_tokens}")
print(f"Stop reason : {final.stop_reason}")
get_final_message() must be called inside the with
block, after the loop. Once the context manager exits, the stream connection
is closed and the final message is no longer accessible.
7.5 Streaming with a System Prompt
The system= parameter works exactly the same way in streaming
calls as in non-streaming calls.
# stream_system.py - streaming with a system prompt.
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=512,
system="You are a Python tutor. Always include type hints and a one-line docstring.",
messages=[{"role": "user", "content": "Write a function that merges two sorted lists."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print()
7.6 When Not to Stream
Streaming is the right choice when a user watches the terminal or a UI in
real time. It adds complexity without benefit in these situations:
| Situation | Use streaming? | Reason |
| Displaying output to a user in real time |
Yes |
Reduces perceived latency |
| Batch processing: save output to a file |
No |
No display - messages.create() is simpler |
| Unit tests asserting on response content |
No |
Tests need the full string; streaming adds no value |
| Parsing JSON from the response |
No |
JSON must be complete before parsing; collect it all first |
7.7 Exercise
Exercise
Modify stream_hello.py to ask for a longer explanation:
change the user message to "Explain how Python's garbage collector works
in detail." Add a counter variable before the loop and increment it by 1
inside the loop on each iteration. After the loop (and after printing the
final newline), print the total chunk count. Run the script and observe how
many chunks a longer response produces compared to a short code snippet.
7.8 Common Mistakes
Using messages.create when you meant messages.stream
client.messages.create() returns the entire response at once as
a Message object. It does not return a stream. If you forget to
use client.messages.stream(), you get no incremental output - just
the usual blocking wait followed by the full response.
Forgetting end="" and flush=True in print()
Without end="", Python appends a newline after each chunk and
the code appears one word per line. Without flush=True, Python
buffers output in memory and releases it in large blocks rather than
immediately - the terminal does not update until the buffer fills, which
defeats the purpose of streaming. Always use both.
Calling get_final_message() inside the loop
get_final_message() blocks until the stream is complete. Calling
it inside the for text in stream.text_stream loop on the first
iteration will consume the rest of the stream internally, causing the loop
to exit early after just one chunk. Call it after the loop finishes.
7.9 Key Terms
| Term | Meaning |
| streaming | Receiving model output incrementally as it is generated, rather than waiting for the complete response |
| text_stream | An iterator on the stream object that yields text chunks as they arrive |
| context manager | A Python with statement that manages setup and teardown; here it opens and closes the stream connection |
| chunk | One piece of text yielded by text_stream; typically a word or a few characters |
| flush | Forcing Python to send buffered output to the terminal immediately instead of waiting |
| get_final_message | Method on the stream object that returns the complete Message with usage stats after streaming ends |
| buffering | Python's default behavior of holding output in memory and writing it in batches; disabled per print() with flush=True |