Everything you need to call the Claude API from Python: the request and the response, streaming, tools, structured outputs, thinking, prompt caching, batches, files, errors and what a call costs. Every fact is checked against the official docs and the anthropic 2.3.0 package, and every example was run against a stub server, apart from the cloud client calls.
It needs Python 3.10 or newer. On Python 3.9, pip quietly installs the old 0.125.0 release instead of 2.x.
STEP 2
Set your key
The client reads ANTHROPIC_API_KEY for you. In 2.x a client holds one credential, and passing two raises ValueError.
STEP 3
Send a message
model, max_tokens and messages are the three required arguments. The reply is a Message with a list of content blocks.
$ pip install anthropic
$ export ANTHROPIC_API_KEY=your-api-key
Python
import anthropic
# Reads ANTHROPIC_API_KEY from the environment.
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
system="Answer in one short paragraph.",
messages=[{"role": "user", "content": "What is a token?"}],
)
# content[0] can be a thinking block, so pick the text blocks.
text = "".join(b.text for b in message.content if b.type == "text")
print(text)
print(message.stop_reason, message.usage.input_tokens, message.usage.output_tokens)
print(message._request_id)
anthropic.Anthropic() is the sync client and anthropic.AsyncAnthropic() is the async one. Both close with client.close() or a with block.
The request ID is message._request_id. Quote it when you ask Anthropic for help. Errors carry it as e.request_id.
Turn on logging with the ANTHROPIC_LOG=debug environment variable.
The API is stateless. Each call carries the whole conversation, so you append every reply to your own list and send it again.
Python
import anthropic
client = anthropic.Anthropic()
history: list[anthropic.types.MessageParam] = []
defask(prompt: str) -> str:
history.append({"role": "user", "content": prompt})
reply = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=history, # the API is stateless: resend everything
)
# Send Claude's blocks back unchanged so thinking blocks stay valid.
history.append({"role": "assistant", "content": reply.content})
return"".join(b.text for b in reply.content if b.type == "text")
print(ask("My name is Sam. Pick a number from 1 to 10."))
print(ask("What is my name, and what number did you pick?"))
Parameter
What it does
model
Required. The full model ID.
max_tokens
Required. A hard cap on output, thinking tokens included. 0 writes the prompt cache and returns no output.
messages
Required. user and assistant turns. Consecutive turns with the same role are merged into one.
system
A string, or a list of text blocks. Blocks can carry cache_control.
stop_sequences
Custom strings that end the reply. A match sets stop_reason to stop_sequence.
metadata
{"user_id": ...}: an opaque ID up to 512 characters. Never a name or an email address.
tools, tool_choice
Tool definitions and how Claude may use them.
thinking, output_config
output_config holds effort and the JSON schema format.
cache_control
Top level automatic prompt caching.
inference_geo
"us" keeps inference in the United States at 1.1x the price. Needs Claude 4.6 or later.
service_tier
"auto" or "standard_only".
speed
"fast" is fast mode for Opus 5.5: $8 input and $40 output per million tokens.
Claude Fable 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5 accept a role: "system" message after a user turn. It has the same authority as the top level system and does not break the cache.
The request limit is 32 MB for messages and token counting, 256 MB for batches and 500 MB for files.
message.content is a list of blocks. Adaptive thinking is on by default, so the first block can be a thinking block with empty text. Never read content[0].text. Pick the blocks whose type is text.
stop_reason
What it means
end_turn
Claude finished its answer.
max_tokens
The reply hit your max_tokens cap and is cut off.
stop_sequence
The reply matched one of your stop_sequences.
tool_use
Claude wants you to run a tool. Answer with a tool_result.
pause_turn
A server tool loop paused. Send the assistant message back unchanged to continue.
refusal
Claude declined. stop_details explains why, and a refusal with no output is not billed.
model_context_window_exceeded
The context window is full.
usage field
What it counts
input_tokens
Input tokens after your last cache breakpoint. With no caching it is all input.
output_tokens
Output tokens, including thinking tokens.
cache_creation_input_tokens
Tokens written to the cache. cache_creation splits them into 5 minute and 1 hour writes.
cache_read_input_tokens
Tokens read from the cache at the discounted rate.
output_tokens_details
thinking_tokens is the share of output spent thinking.
server_tool_use
web_search_requests and web_fetch_requests.
service_tier, inference_geo
Which tier and region served the request.
Total input is input_tokens plus cache_creation_input_tokens plus cache_read_input_tokens.
For headers such as retry-after and the rate limit counters, call client.messages.with_raw_response.create(...), read .headers, and use .parse() to get the Message.
Eight behaviors that break code written for older SDKs or older models, each with the one line fix.
SDK 2.xOct 21, 2026
01
temperature, top_p and top_k are gone
SDK 2.x has no such arguments, so temperature=0.2 raises TypeError. API version 2026-10-21 returns a 400 for them on every model, even at the default value.
Do thisDelete them. Steer the reply with the prompt and output_config={"effort": ...}.
Streaming, tools, structured outputs and thinking.
Stream the reply
Stream text as it arrives
Use the stream helper for text. It is a context manager, and it can hand you the complete message at the end.
Streaming
Python
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environmentwith client.messages.stream(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a haiku about APIs."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message() # same Message object create() returnsprint(f"\nstop_reason={final.stop_reason} output_tokens={final.usage.output_tokens}")
stream.text_stream yields only the text pieces. stream.get_final_message() returns the same Message that create() would.
The stream is cancelled when the with block exits. stream.close() ends it early.
An error can arrive mid stream as an error event, for example overloaded_error, so normal exception handling does not cover everything. Ignore event types you do not know.
The async client: async with, async for, await get_final_message().
Tool input streams as partial JSON strings. Join them and parse once at content_block_stop. The final tool_use.input is always an object.
To resume a cut stream on Claude 4.6 and later, send a user message that holds the partial reply and asks Claude to continue. Tool use and thinking blocks cannot be resumed.
Tool use
Run the tool loop
Claude asks for a tool, you run it, and you send the result back. Repeat until stop_reason is no longer tool_use.
The tool loop
Python
import anthropic
client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Get the current weather for a city. Returns temperature and conditions.",
"input_schema": {"type": "object", "required": ["city"],
"properties": {"city": {"type": "string", "description": "e.g. Paris"}}},
}]
handlers = {"get_weather": lambda args: f"18C and cloudy in {args['city']}"}
messages = [{"role": "user", "content": "What is the weather in Paris?"}]
whileTrue:
response = client.messages.create(
model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages
)
if response.stop_reason != "tool_use":
break
messages.append({"role": "assistant", "content": response.content}) # keeps thinking blocks
results = []
for block in response.content:
if block.type == "tool_use":
try:
output, is_error = handlers[block.name](block.input), Falseexcept Exception as exc: # tell the model what failed
output, is_error = str(exc), True
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": output, "is_error": is_error})
messages.append({"role": "user", "content": results})
print(next(b.text for b in response.content if b.type == "text"))
A tool is a name, a description and an input_schema. Optional fields include strict, cache_control and input_examples.
Reply with a new user message of tool_result blocks: tool_use_id, content and optional is_error. The tool_result blocks must come first in that message.
Parallel tool use is on by default. Return one tool_result for every tool_use in the same message.
is_error: true tells Claude the tool failed, and a clear error message helps it recover.
Keep third party text inside tool_result blocks, never in the system prompt. Claude treats tool results as untrusted input.
tool_choice takes auto, any, tool and none. Switch off parallel calls inside that object.
Python
import anthropic
client = anthropic.Anthropic()
weather_tool = {
"name": "get_weather",
"description": "Get the current weather for a city. Returns temperature and conditions.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
tools=[weather_tool],
# "auto" (default) and "none" work everywhere; "any" and "tool" return 400 on Sonnet 5.5
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=[{"role": "user", "content": "Compare the weather in Paris and Rome."}],
)
calls = [b for b in response.content if b.type == "tool_use"]
print(response.stop_reason, len(calls)) # at most 1 call with parallel use disabled
disable_parallel_tool_use lives inside tool_choice. It is not a top level parameter.
On Opus 5.5, Sonnet 5.5 and Fable 5.1, any and tool return a 400. Use auto, plus strict tools or structured outputs.
strict: true on a tool makes the sampler match your JSON schema. It is generally available with no beta header. The limits are 20 strict tools and 24 optional parameters per request.
The tool runner is a beta helper. It calls your decorated functions and feeds the results back until Claude is done.
Python
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
defget_weather(city: str) -> str:
"""Get the current weather for a city.
Args:
city: City name, for example Paris.
"""returnf"18C and cloudy in {city}"
runner = client.beta.messages.tool_runner( # beta helper: runs the tool loop for you
model="claude-sonnet-5-5",
max_tokens=1024,
tools=[get_weather],
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
)
final = runner.until_done() # or: for message in runner: ...print(next(b.text for b in final.content if b.type == "text"))
It lives at client.beta.messages.tool_runner and the decorator is @beta_tool. The schema comes from the function signature and docstring.
It keeps a server side message thread by default and adds the beta header message-threads-2026-08-12 itself. Pass thread=False to resend the full history each time.
Raise anthropic.lib.tools.ToolError to report a failure to Claude. Cap the loop with max_iterations.
Server tools run on Anthropic servers. You add the tool and read the results out of the reply.
Python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=2048,
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 3}],
messages=[{"role": "user", "content": "What did Anthropic announce this week?"}],
)
for block in response.content:
if block.type == "server_tool_use": # Claude's search queryprint("search:", block.input["query"])
elif block.type == "text":
print(block.text)
for cite in block.citations or []:
print(" source:", cite.title, cite.url)
if response.stop_reason == "pause_turn": # long turn: send this message back unchanged to continueprint("paused")
print("searches billed:", response.usage.server_tool_use.web_search_requests)
Web search costs $10 per 1,000 searches plus normal token costs. A failed search is not billed. usage.server_tool_use.web_search_requests counts them.
The reply holds a server_tool_use block, a web_search_tool_result block, then text blocks with citations. Send assistant content back exactly as received, including encrypted_content.
A search failure returns HTTP 200 with an error object inside the result. When stop_reason is pause_turn, send the assistant message back to continue.
web_search_20260209 and later filter results with code, and need allowed_callers set to ["direct"] for a plain search on models without programmatic tool calling.
Web fetch has no extra charge beyond tokens, and its citations are off until you set "citations": {"enabled": true}.
Code execution is generally available. It is free when used with web_search_20260209 or later or web_fetch_20260209 or later, and otherwise bills by execution time after 1,550 free hours a month.
Connect a remote MCP server
The MCP connector is still beta. Claude calls tools on a remote server for you.
Python
import os
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create( # MCP connector is beta
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "What tools do you have available?"}],
mcp_servers=[{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse", # remote HTTP/SSE only"name": "example-mcp",
"authorization_token": os.environ.get("MCP_TOKEN", ""),
}],
tools=[{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}],
betas=["mcp-client-2025-11-20"],
)
for block in response.content:
if block.type == "mcp_tool_use":
print("called:", block.server_name, block.name, block.input)
elif block.type == "text":
print(block.text)
Call client.beta.messages.create with betas=["mcp-client-2025-11-20"], an entry in mcp_servers and an mcp_toolset entry in tools.
Only tool calls work, not prompts or resources. The server must be public and use Streamable HTTP or SSE, so a local stdio server cannot be connected.
It is not eligible for zero data retention. For local servers use the SDK client side MCP helpers with the tool runner.
A newer beta header, mcp-client-2026-09-15, pins each server's tool list. The snippet was checked against a stub server, not a live MCP server.
Turn on citations for a document and each claim in the reply points back to the passage it came from.
Python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{
"type": "document",
"source": {"type": "text", "media_type": "text/plain",
"data": "The grass is green. The sky is blue."},
"title": "Colors",
"citations": {"enabled": True},
},
{"type": "text", "text": "What color is the sky?"},
],
}],
)
for block in response.content:
if block.type == "text":
print(block.text)
for cite in block.citations or []: # char_location for plain text documentsprint(f" [{cite.start_char_index}:{cite.end_char_index}] {cite.cited_text}")
Set "citations": {"enabled": true} on all of the documents in a request, or on none.
Plain text gives char_location citations, PDFs give page_location and custom content gives content_block_location.
Citations cannot be combined with structured outputs: that returns a 400. Under API version 2026-10-21, a citations array on a user message text block also returns a 400.
Structured outputs are generally available with no beta header. Pass a schema in output_config, or let the SDK build one from a Pydantic class.
Python
import json
import anthropic
from pydantic import BaseModel
client = anthropic.Anthropic()
classContact(BaseModel):
name: str
email: str
wants_demo: bool
prompt = "Extract: John Smith (john@example.com) wants a demo next Tuesday."# SDK helper: sends output_config.format for you and parses the reply
parsed = client.messages.parse(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
output_format=Contact,
)
print(parsed.parsed_output)
# Raw JSON schema: no helper, you parse the text block yourself
raw = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
output_config={"format": {"type": "json_schema", "schema": {
**Contact.model_json_schema(), "additionalProperties": False}}},
)
print(json.loads(next(b.text for b in raw.content if b.type == "text")))
client.messages.parse(..., output_format=Model) returns the object at response.parsed_output. Without the helper, read the JSON from the first text block.
The old top level output_format dict raises TypeError in this SDK. Use output_config={"format": {...}}.
Objects need additionalProperties: false. Recursive schemas, numeric limits and string length limits are not supported. A schema Claude cannot compile returns a 400.
The first request with a new schema is slower, and the compiled grammar is cached for 24 hours. A refusal or max_tokens can still leave output that does not match.
It works with streaming, batches, token counting and strict tools. It does not work with citations or prefill.
The raw schema branch follows the docs and was checked against a stub server. It was not sent to the live API.
Current models think adaptively. You choose how much with effort, and whether you see a summary with display.
Python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=16000, # thinking tokens count toward max_tokens
thinking={"type": "adaptive", "display": "summarized"}, # budget_tokens is a 400 here
output_config={"effort": "high"}, # low | medium | high | xhigh | max
messages=[{"role": "user", "content": "What is the GCD of 1071 and 462?"}],
)
for block in response.content:
if block.type == "thinking":
print("thinking:", block.thinking) # empty string if display is "omitted"elif block.type == "text":
print("answer:", block.text)
print("billed output tokens (thinking included):", response.usage.output_tokens)
output_config={"effort": ...} accepts low, medium, high, xhigh and max, though not every model supports xhigh or max. Effort is soft guidance. max_tokens is the hard limit.
Thinking tokens are billed as output tokens even when the text is omitted. usage.output_tokens_details.thinking_tokens shows the share.
display is "summarized" or "omitted". On Sonnet 5.5, Opus 5.5 and Haiku 5.5 the default is omitted, so you get an empty thinking block.
During a tool loop, send the thinking blocks back complete and unmodified with their tool_use block.
Forcing a tool skips thinking. Sampling parameters and budget_tokens are rejected on current models.
This request shape follows the docs and was checked against a stub server. It was not sent to the live API.
Not allowed. {"type": "between_tools"} is the lowest setting, accepted at effort high or below.
Opus 5.5, Fable 5.1
Adaptive, always on
Not allowed.
Haiku 5.5
Adaptive
disabled works at effort high or below.
Opus 4.5, Sonnet 4.5, Haiku 4.5
Off
Only enabled with budget_tokens of at least 1,024 works.
Topic 4 of 6
Cost and scale
Caching, batches, files, images and what a call costs.
Prompt caching
Cache a long prefix
Mark the stable start of your prompt. Later requests that begin with the same prefix read it at a fraction of the price.
Prompt caching
Python
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
handbook = open("handbook.txt").read() # must reach 512 tokens on the current models
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=512,
# Automatic caching instead: pass cache_control={"type": "ephemeral"} at the top level.
system=[
{
"type": "text",
"text": handbook,
"cache_control": {"type": "ephemeral", "ttl": "1h"}, # ttl is "5m" or "1h"
}
],
messages=[{"role": "user", "content": "Summarize the refund policy."}],
)
u = response.usage
print("written:", u.cache_creation_input_tokens, "read:", u.cache_read_input_tokens)
print("by ttl:", u.cache_creation) # ephemeral_5m_input_tokens, ephemeral_1h_input_tokens
Automatic caching is one top level cache_control={"type": "ephemeral"}. The breakpoint moves forward as the chat grows. For control, put cache_control on a block.
The default lifetime is 5 minutes and every read refreshes it. Add "ttl": "1h" for an hour, written at 2x the base input price.
A request can carry at most 4 breakpoints, and an automatic one uses a slot. The lookback is 20 blocks.
The order is tools, then system, then messages. A change at one level invalidates it and everything after it.
Changing tool definitions, tool_choice, images or thinking settings breaks the cache for what follows.
max_tokens: 0 warms the cache and returns no output. It is not allowed with streaming, extended thinking, structured outputs, forced tool_choice or inside a batch.
Prices are per million tokens. A write is 1.25x input for 5 minutes and 2x for an hour. A read is 0.1x input, except 0.025x on Fable 5.1 and 0.05x on Opus 5.5 and Sonnet 5.5.
The minimum cacheable prompt is 512 tokens on all four current models. A shorter prompt is processed with no caching and no error.
A 5 minute write pays for itself after one read, and a 1 hour write after two.
On the Claude API caches are isolated per workspace (per organization on Bedrock and Google managed Vertex), and a hit needs a 100 percent identical prefix.
Message batches
Batches run requests asynchronously at a 50 percent discount on input and output tokens. Most finish within an hour.
Python
import anthropic
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
client = anthropic.Anthropic()
questions = ["What is a vector database?", "What is RAG?", "What is MCP?"]
batch = client.messages.batches.create(
requests=[
Request(
custom_id=f"q-{i}", # 1 to 64 chars: letters, digits, - and _
params=MessageCreateParamsNonStreaming(
model="claude-haiku-5-5",
max_tokens=300,
messages=[{"role": "user", "content": question}],
),
)
for i, question inenumerate(questions)
]
)
print(batch.id, batch.processing_status, batch.expires_at) # in_progress, expires in 24h
Python
import sys
import time
import anthropic
client = anthropic.Anthropic()
batch_id = sys.argv[1] # msgbatch_...while client.messages.batches.retrieve(batch_id).processing_status != "ended":
time.sleep(60) # poll; client.messages.batches.cancel(batch_id) stops it earlyfor item in client.messages.batches.results(batch_id): # order is not guaranteed
outcome = item.result
if outcome.type == "succeeded":
text = "".join(b.text for b in outcome.message.content if b.type == "text")
print(item.custom_id, text[:80])
else: # errored, canceled or expired: none of these are billedprint(item.custom_id, outcome.type)
A batch holds up to 100,000 requests or 256 MB. It expires after 24 hours, and results stay downloadable for 29 days.
custom_id matches ^[a-zA-Z0-9_-]{1,64}$ and must be unique. Results come back in any order, so match on custom_id.
Result types are succeeded, errored, canceled and expired. The last three are not billed.
Batches support vision, tools, thinking and system prompts. They do not support streaming, speed or max_tokens: 0.
Caching works in batches on a best effort basis. Write a 1 hour entry first, then send the rest.
from pathlib import Path
import anthropic
client = anthropic.Anthropic()
# Out of beta: client.files, no beta header. 500 MB per file.
uploaded = client.files.upload(file=Path("report.pdf"), expires_in_seconds=86400)
print(uploaded.id, uploaded.size_bytes, uploaded.expires_at)
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": [
{"type": "document", "source": {"type": "file", "file_id": uploaded.id}},
{"type": "text", "text": "List the three biggest risks."},
],
}
],
)
print("".join(b.text for b in response.content if b.type == "text"))
client.files.delete(uploaded.id)
The Files API is generally available: use client.files, no beta header.
Python
import base64
from pathlib import Path
import anthropic
client = anthropic.Anthropic()
data = base64.standard_b64encode(Path("chart.png").read_bytes()).decode()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": [
# Image first, then the question. Or use {"type": "url", "url": "https://..."}.
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": data}},
{"type": "text", "text": "What trend does this chart show?"},
],
}
],
)
print("".join(b.text for b in response.content if b.type == "text"))
print("input tokens:", response.usage.input_tokens)
Python
import base64
from pathlib import Path
import anthropic
client = anthropic.Anthropic()
pdf = base64.standard_b64encode(Path("contract.pdf").read_bytes()).decode()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "document", # PDF first, then the question"source": {"type": "base64", "media_type": "application/pdf", "data": pdf},
"cache_control": {"type": "ephemeral"}, # reuse it across questions
},
{"type": "text", "text": "What are the termination terms? Cite page numbers."},
],
}
],
)
print("".join(b.text for b in response.content if b.type == "text"))
A file is up to 500 MB, and an organization can store 1 TB. expires_in_seconds runs from 3,600 to 7,776,000. You cannot download a file you uploaded.
Reference a file with {"type": "file", "file_id": ...} inside a document or image block. File content in a request is billed as input tokens, and the file calls themselves are free.
Images take ceil(width / 28) * ceil(height / 28) tokens, up to 4,784 on the four current models. A request can hold 600 images, and the whole request is capped at 32 MB.
Put images and PDFs before the question. Use JPEG, PNG, GIF or WebP.
A PDF can have 600 pages, or 100 when the request has a context window under 1M tokens. Each page costs text tokens plus image tokens.
import anthropic
client = anthropic.Anthropic()
# Free to call, but it has its own requests-per-minute limit.
count = client.messages.count_tokens(
model="claude-haiku-5-5",
system="You are a careful reviewer.",
messages=[{"role": "user", "content": "Review this paragraph for clarity."}],
)
print(count.input_tokens)
# Recount per model: Claude 4.7 and later use a newer tokenizer.if count.input_tokens > 100_000:
raiseSystemExit("Prompt too long for the Haiku 5.5 low price band")
import anthropic
# Defaults: max_retries=2, timeout=600s (10 minutes).
client = anthropic.Anthropic(max_retries=4, timeout=60.0)
try:
message = client.with_options(max_retries=1).messages.create(
model="claude-haiku-5-5",
max_tokens=256,
messages=[{"role": "user", "content": "Say hello."}],
)
print(message._request_id)
except anthropic.RateLimitError as e: # 429print("slow down", e.response.headers.get("retry-after"), e.request_id)
except anthropic.OverloadedError as e: # 529print("API overloaded", e.request_id)
except anthropic.APITimeoutError: # subclass of APIConnectionError, so catch it firstprint("timed out")
except anthropic.APIConnectionError as e:
print("network problem", e.__cause__)
except anthropic.APIStatusError as e: # any other 4xx or 5xxprint(e.status_code, e.request_id)
Status
Exception
Meaning
400
BadRequestError
The request is malformed or uses something the model rejects.
401
AuthenticationError
The key is wrong or missing.
403
PermissionDeniedError
The key cannot use that resource.
404
NotFoundError
The resource or model does not exist.
413
RequestTooLargeError
The request is over the size limit.
429
RateLimitError
You hit a rate limit or your spend cap.
500, 504
InternalServerError, DeadlineExceededError
Something failed on the server side.
529
OverloadedError
The API is overloaded for everyone.
The SDK retries 2 times by default, with a short backoff that honors retry-after. It retries connection errors, 408, 409, 429 and every 5xx, 529 included.
The default timeout is 10 minutes. Change both with Anthropic(max_retries=0, timeout=20.0), or per call with client.with_options(max_retries=5, timeout=5.0).
A 429 caused by a monthly spend cap has no retry-after and retrying never helps until the cap resets on the 1st at 00:00 UTC.
Limits are measured as requests, input tokens and output tokens per minute for each model class. The response headers anthropic-ratelimit-* show what is left.
The SDK now uses httpx2. A custom http_client must be built from httpx2, and an httpx client raises TypeError.
import os
from anthropic import AnthropicBedrockMantle, AnthropicFoundry, AnthropicVertex
# pip install "anthropic[bedrock]" "anthropic[vertex]"; Foundry ships in the base package.
bedrock = AnthropicBedrockMantle(aws_region="us-east-1")
vertex = AnthropicVertex(project_id=os.environ["GOOGLE_CLOUD_PROJECT"], region="global")
foundry = AnthropicFoundry(
api_key=os.environ["ANTHROPIC_FOUNDRY_API_KEY"],
resource=os.environ["FOUNDRY_RESOURCE"],
)
# Same messages.create() on every client; only the model ID differs on Bedrock.for client, model in [
(bedrock, "anthropic.claude-sonnet-5-5"),
(vertex, "claude-sonnet-5-5"),
(foundry, "claude-sonnet-5-5"), # your Foundry deployment name
]:
reply = client.messages.create(
model=model,
max_tokens=256,
messages=[{"role": "user", "content": "Hello!"}],
)
print(model, reply.stop_reason)
Platform
Python client
Model ID for Opus 5.5
Amazon Bedrock, new projects
AnthropicBedrockMantle
anthropic.claude-opus-5-5
Amazon Bedrock, InvokeModel
AnthropicBedrock
Varies by model, see the Bedrock page
Google Vertex AI
AnthropicVertex
claude-opus-5-5
Microsoft Foundry
AnthropicFoundry
Your deployment name, which defaults to claude-opus-5-5
Install the extras you need, for example pip install "anthropic[bedrock]" or "anthropic[vertex]".
AnthropicBedrock raises ValueError when no AWS region is set. It no longer falls back to us-east-1.
Each platform sets its own prices and retirement dates. Bedrock regional endpoints cost 10 percent more than global ones.
Prompt caching and the Files API do not behave the same everywhere. Check the platform page before you rely on them.
All dates are 2026. These libraries release often, so read each changelog before you take a minor release.
Questions people ask
Why does `temperature` raise a TypeError?+
SDK 2.x removed temperature, top_p and top_k. API version 2026-10-21 also returns a 400 for them on every model. Delete them and steer the reply with the prompt and output_config={"effort": ...}.
Why is the first content block empty?+
Adaptive thinking is on by default and the thinking text is omitted, so the first block can be a thinking block with no text. Pick the blocks where type is text.
Can I force Claude to call a tool?+
Not on Opus 5.5, Sonnet 5.5 or Fable 5.1: tool_choice of any or a named tool returns a 400 there. Use auto with strict tools, or structured outputs when you only need JSON.
How do I stream a long answer?+
Use client.messages.stream(...) and read text_stream. The SDK raises ValueError for a non streaming request with max_tokens above 21,333, so long outputs need the stream helper and get_final_message().
How do I cut the cost of a big prompt?+
Cache the stable prefix. A read costs 0.1x input on most models, 0.05x on Opus 5.5 and Sonnet 5.5, and 0.025x on Fable 5.1. For work that can wait, a batch halves the price, and both stack.
Does the API remember my conversation?+
No. It is stateless. You send the whole history on every call. The beta tool runner keeps a server side thread by default, and thread=False turns that off.
How do I count tokens before I send a request?+
client.messages.count_tokens(...) takes the same inputs as create and is free. It returns an estimate, and it does not work with server tools or url sources.
Which model should I start with?+
Anthropic's docs say to start with Opus 5.5 for most workloads. Sonnet 5.5 is the best mix of speed and intelligence and the model the SDK examples use. Haiku 5.5 is for high volume, latency sensitive work, and Fable 5.1 is for the hardest reasoning.
Sources
Versions come from the package registries. Behavior, prices and dates come from the official docs and changelogs, all read on Oct 28, 2026. The code examples were checked against the versions at the top of the page.