Anthropic API: Messages, Tool Use, Streaming and Caching Reference Guide
By DevShelfHub
Messages API, system prompts, tool use, streaming, vision, prompt caching, extended thinking, batch — the Claude SDK as a one-page reference for anthropic-python 0.40+ and anthropic-node 0.30+. Covers every request parameter and the beta header pattern for enabling new features.
All current Claude features live on the Messages API (/v1/messages).
The old /v1/complete Text Completions endpoint is legacy — avoid for new code.
The SDK reads ANTHROPIC_API_KEY from env. Beta features
(prompt-caching, extended-thinking,
message-batches) toggle via the anthropic-beta header
or SDK helpers like client.beta.messages.create.
Server-side web search. Results spliced into the response.
Worked example
python
from anthropic import Anthropic
client = Anthropic()
tools = [{
"name": "get_weather",
"description": "Current weather for a city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Weather in Mumbai?"}],
)
# Loop until the model stops requesting tools
while resp.stop_reason == "tool_use":
tool_use = next(b for b in resp.content if b.type == "tool_use")
result = {"temp_c": 31, "city": tool_use.input["city"]}
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "Weather in Mumbai?"},
{"role": "assistant", "content": resp.content},
{"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": str(result),
}]},
],
)
print(resp.content[0].text)
Token-by-tokenStreaming
with client.messages.stream(…) as stream:
Preferred Context manager — auto-closes the SSE connection.
for text in stream.text_stream:
Iterator of text deltas only. The shortcut.
for event in stream:
Iterator of all events (text, tool, message_stop).
stream.get_final_message()
Block-wait for the full Message after iterating.
client.messages.create(…, stream=True)
Raw iterator. Manual close required.
Event types
message_start
Opens with empty message + initial usage.
content_block_start / _delta / _stop
One trio per block. Watch delta.text or delta.partial_json.
message_delta
Updates stop_reason + final usage.
message_stop
Stream is done. Safe to read final state.
Worked example
python
from anthropic import Anthropic
client = Anthropic()
# Context-manager form — auto-closes the SSE connection
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Haiku about Mumbai monsoons."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print(f"\n[done · {final.usage.input_tokens}+{final.usage.output_tokens} tokens]")
10% of input costPrompt caching
cache_control={"type":"ephemeral"}
Mark a block as cacheable. 5-minute TTL.
cache_control={"type":"ephemeral","ttl":"1h"}
1-hour TTL. Premium pricing.
Place on system, tools, or messages blocks
Last block with cache_control becomes the cache boundary.
resp.usage.cache_creation_input_tokens
Tokens billed at write-rate (first call).
resp.usage.cache_read_input_tokens
Tokens billed at 10% on cache hits.
Up to 4 cache breakpoints per request
Chain them for nested reusable prefixes.
Caching is prefix-only. Anything before the marked block must be byte-identical across calls.
Reorder a system prompt and the cache invalidates.
Worked example
python
from anthropic import Anthropic
client = Anthropic()
# Mark expensive prefix content as cacheable.
# Anthropic auto-matches on subsequent calls and bills cached input at 10%.
LONG_DOC = open("policy_handbook.md").read() # ~50k tokens
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system=[
{"type": "text", "text": "You answer using only the handbook below."},
{
"type": "text",
"text": LONG_DOC,
"cache_control": {"type": "ephemeral"}, # 5-min TTL
},
],
messages=[{"role": "user", "content": "What's the leave policy?"}],
)
# Watch usage to confirm a cache hit
print(resp.usage.cache_creation_input_tokens) # > 0 on first call
print(resp.usage.cache_read_input_tokens) # > 0 on subsequent calls
Reasoning budgetExtended thinking
thinking={"type":"enabled","budget_tokens":4096}
Enable for hard problems. Budget ≤ max_tokens.
temperature=1.0 (required with thinking)
Can’t combine extended thinking with low temperature.
{"type":"thinking","thinking":"…"}
First block of response is the thinking trace.
{"type":"redacted_thinking","data":"…"}
Encrypted trace — pass back unchanged on the next turn.
Round-trip thinking blocks in tool loops
Drop them and the model loses its chain. Reflect them in assistant content.
50% off · 24h SLAMessage Batches
client.messages.batches.create(requests=[…])
Submit up to 100k requests. Each has a custom_id.
client.messages.batches.retrieve(batch_id)
Poll for processing_status.
client.messages.batches.results(batch_id)
Stream JSONL results once ended.
client.messages.batches.list()
All recent batches for your workspace.
client.messages.batches.cancel(batch_id)
Abort in-flight batch.
Production hygieneErrors & retries
Anthropic(api_key=…, max_retries=4, timeout=30.0)
Client-level retry + timeout. Defaults are 2 / 600.
Hand-rolled tool loop: call Claude, run tool, feed result back, repeat until stop_reason != "tool_use".
No framework — just the SDK.
python
from anthropic import Anthropic
client = Anthropic()
tools = [{
"name": "lookup_order",
"description": "Find an order by ID.",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
}]
def run_tool(name: str, args: dict) -> str:
if name == "lookup_order":
return f"Order {args['order_id']}: shipped, ETA Tue."
return "unknown tool"
messages = [{"role": "user", "content": "Where is order ORD-71?"}]
while True:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
print(next(b.text for b in resp.content if b.type == "text"))
break
tool_results = [
{
"type": "tool_result",
"tool_use_id": b.id,
"content": run_tool(b.name, b.input),
}
for b in resp.content if b.type == "tool_use"
]
messages.append({"role": "user", "content": tool_results})
Best practiceGood to know
Cache long static prefixes. System prompts, knowledge-base context, tool schemas — anything reused across calls.
Cached reads are 10% of normal input cost; a 50k-token handbook becomes nearly free after the first hit.
Inspect resp.content as a list, not a string.
Tool use, vision answers, and extended thinking all add non-text blocks. Filter by b.type == "text" when you want the prose.
Use Haiku 4.5 for routing. Tool selection, classification, simple extraction don’t need Sonnet/Opus. Drop the latency and cost by tier.
Common trapsWatch out for
max_tokens is required.
Forget it and the SDK errors. Set a real ceiling — default to 1024 unless you have a reason.
Tool schema field is input_schema, not parameters.
Copy-pasting an OpenAI tool definition won’t parse. Anthropic uses input_schema at the top level — no function wrapper.
Messages must alternate user / assistant and start with user.
Two user turns in a row, or starting with assistant, returns a 400. Merge consecutive same-role turns before sending.
The Messages API (POST /v1/messages) is the primary way to call Claude. You pass a model, max_tokens, and a messages array with alternating user and assistant turns. The API returns a Message object with content blocks and usage stats.
How do I enable tool use with the Claude API?
Pass a tools array to client.messages.create(). Each tool needs a name, description, and input_schema (JSON Schema). When Claude decides to use a tool, it returns a tool_use content block; you call the tool and return the result in a tool_result block.
What is prompt caching in the Anthropic API?
Prompt caching lets you mark a content block with cache_control: {type: 'ephemeral'} so the API caches that prefix for up to 5 minutes. Cached tokens cost 10% of normal input price and reduce latency on repeated calls with the same system prompt or documents.
How do I stream Claude responses?
Use the client.messages.stream() context manager in Python. It yields events including text_delta, content_block_start, and message_stop. Call stream.get_final_message() after the loop to get the complete Message object.
What Claude models are available via the Anthropic API?
Current models include claude-opus-4-8 (most capable), claude-sonnet-4-6 (balanced quality and speed), and claude-haiku-4-5-20251001 (fastest and cheapest). Choose Haiku for classification and routing, Sonnet as a strong default, and Opus for complex reasoning.
Does the Anthropic API support vision and image inputs?
Yes. Pass an image content block with type: image and source set to either base64-encoded data (with media_type) or a URL. Claude supports JPEG, PNG, GIF, and WebP. Vision is available on all current Claude models.