Cheat sheet · Checked Oct 28, 2026

Claude API in Python cheat sheet

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.

anthropic for Python · released Oct 23, 2026
anthropic 2.3.0
API version · released Oct 21, 2026
anthropic-version: 2026-10-21
A clay diorama of a laptop sending an envelope to a friendly terracotta orb, with a stream of glowing beads flowing out to a stack of cards

Explore with AI

Browse by topic

Topic 1 of 6

Get started

Install, pick a model, send your first request.

Install and make the first call

STEP 1

Install the package

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.

Sources: Python SDK docs · anthropic on PyPI · Messages API reference

Pick a model

ModelAPI IDInputOutputCache readNote
Claude Fable 5.1claude-fable-5-1$10$50$0.25Demanding reasoning and long horizon work
Claude Opus 5.5claude-opus-5-5$4$20$0.20Anthropic says to start here for most workloads. Default effort is medium
Claude Sonnet 5.5claude-sonnet-5-5$2$10$0.10The best mix of speed and intelligence
Claude Haiku 5.5claude-haiku-5-5$0.10$0.50$0.01For high volume, latency sensitive work. Above 100,000 prompt tokens it is $0.50 in and $2.50 out
  • Prices are per million tokens. All four models have a 1M token context window and up to 128K output tokens.
  • Every ID is a pinned snapshot. The API has no short aliases such as sonnet for these models, so pass the full ID.
  • client.models.list() returns each model with max_input_tokens, max_tokens and a capabilities object, so you can check limits in code.
  • Claude 4.7 and later use a newer tokenizer that produces about 30 percent more tokens for the same text. Recount prompts when you switch.
  • claude-sonnet-4-5-20250929 is deprecated and retires on Nov 30, 2026. Move to claude-sonnet-5-5.
  • Earliest retirement dates: Fable 5.1 Sep 1, 2027, Opus 5.5 Sep 22, 2027, Sonnet 5.5 Sep 28, 2027, Haiku 5.5 Oct 7, 2027. Anthropic gives at least 60 days notice.

Sources: Models overview · Pricing · Model deprecations

The request, and multi turn chats

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] = []


def ask(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?"))
ParameterWhat it does
modelRequired. The full model ID.
max_tokensRequired. A hard cap on output, thinking tokens included. 0 writes the prompt cache and returns no output.
messagesRequired. user and assistant turns. Consecutive turns with the same role are merged into one.
systemA string, or a list of text blocks. Blocks can carry cache_control.
stop_sequencesCustom 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_choiceTool definitions and how Claude may use them.
thinking, output_configoutput_config holds effort and the JSON schema format.
cache_controlTop 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.

Sources: Working with messages · Messages API reference · API errors

Topic 2 of 6

Core concepts

Read the response, and the defaults that bite.

Read the response

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_reasonWhat it means
end_turnClaude finished its answer.
max_tokensThe reply hit your max_tokens cap and is cut off.
stop_sequenceThe reply matched one of your stop_sequences.
tool_useClaude wants you to run a tool. Answer with a tool_result.
pause_turnA server tool loop paused. Send the assistant message back unchanged to continue.
refusalClaude declined. stop_details explains why, and a refusal with no output is not billed.
model_context_window_exceededThe context window is full.
usage fieldWhat it counts
input_tokensInput tokens after your last cache breakpoint. With no caching it is all input.
output_tokensOutput tokens, including thinking tokens.
cache_creation_input_tokensTokens written to the cache. cache_creation splits them into 5 minute and 1 hour writes.
cache_read_input_tokensTokens read from the cache at the discounted rate.
output_tokens_detailsthinking_tokens is the share of output spent thinking.
server_tool_useweb_search_requests and web_fetch_requests.
service_tier, inference_geoWhich 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.

Sources: Messages API reference · Python SDK docs

Defaults that bite

Eight behaviors that break code written for older SDKs or older models, each with the one line fix.

  1. SDK 2.xOct 21, 2026

    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": ...}.

    Migrating to API version 2026-10-21

  2. All current models

    content[0].text can be 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.

    Do thisLoop over message.content and use the blocks where b.type == "text".

    Thinking

  3. Claude 4.6 and later

    Prefilling the reply returns a 400

    A last assistant message is rejected on Claude 4.6 and every later model.

    Do thisPut the format in the system prompt, or use structured outputs.

    API errors · Working with messages

  4. Opus 5.5, Sonnet 5.5, Fable 5.1

    Forced tool choice returns a 400

    tool_choice of any or a named tool is rejected on Opus 5.5, Sonnet 5.5 and Fable 5.1. auto and none still work.

    Do thisUse auto with strict tools or structured outputs.

    Define tools

  5. Sonnet 5.5, Opus 5.5, Fable 5.1

    Thinking cannot always be switched off

    {"type": "disabled"} and budget_tokens return a 400 on Sonnet 5.5. Opus 5.5 and Fable 5.1 always think.

    Do thisUse {"type": "adaptive"}. On Sonnet 5.5 the lowest setting is {"type": "between_tools"}, accepted at effort high or below.

    Thinking

  6. Install

    Python 3.9 installs an old SDK

    pip install anthropic on Python 3.9 silently resolves to 0.125.0, and the 2.x examples fail on it.

    Do thisUse Python 3.10 or newer and check anthropic.__version__.

    anthropic on PyPI

  7. Prompt caching

    Short prompts skip the cache silently

    Under 512 tokens on the four current models nothing is cached. There is no error. Both cache fields read 0.

    Do thisCache a prefix of at least 512 tokens and check cache_read_input_tokens.

    Prompt caching

  8. Long output

    Big max_tokens needs streaming

    The SDK raises ValueError for a non streaming request with max_tokens above 21,333. The API itself does not limit it.

    Do thisUse client.messages.stream(...) and get_final_message().

    Streaming messages

Topic 3 of 6

Build features

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.

Glowing beads flow through a clear pipe from an orb into a row of cups
Streaming
Python
import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from the environment

with 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() returns

print(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.

Sources: Streaming messages · Python SDK docs

Python
import anthropic

client = anthropic.Anthropic()

stream = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    stream=True,  # returns raw events, no accumulated Message
    messages=[{"role": "user", "content": "Say hello in three languages."}],
)

for event in stream:
    if event.type == "message_start":
        print("input tokens:", event.message.usage.input_tokens)
    elif event.type == "content_block_delta":
        delta = event.delta
        if delta.type == "text_delta":
            print(delta.text, end="", flush=True)
        elif delta.type == "input_json_delta":  # partial JSON for a tool_use block
            print(delta.partial_json, end="")
        elif delta.type == "thinking_delta":
            print(delta.thinking, end="")
    elif event.type == "message_delta":  # usage here is cumulative
        print(f"\nstop_reason={event.delta.stop_reason} output={event.usage.output_tokens}")
    elif event.type == "message_stop":
        print("done")
Raw events with create(stream=True): lower memory, because no final message is built.
EventWhat it carries
message_startThe empty message and its usage so far.
content_block_start / stopOne block opens and closes. Each block has its own index.
content_block_deltatext_delta, input_json_delta (partial tool input), thinking_delta, signature_delta, citations_delta.
message_deltaThe final stop_reason and cumulative usage.
message_stopThe stream is over. A ping can arrive at any point.
Python
import asyncio

import anthropic

client = anthropic.AsyncAnthropic()


async def main() -> None:
    async with client.messages.stream(
        model="claude-sonnet-5-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Say hello there!"}],
    ) as stream:
        async for text in stream.text_stream:
            print(text, end="", flush=True)
        message = await stream.get_final_message()
    print(f"\n{message.usage.output_tokens} output tokens")


asyncio.run(main())
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.

A glowing card travels from an orb to a small machine and back
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?"}]
while True:
    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), False
            except 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.

Sources: Define tools · Handle tool calls · Parallel tool use

Control which tool Claude uses

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.

Sources: Define tools · Strict tool use

Let the SDK run the loop

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
def get_weather(city: str) -> str:
    """Get the current weather for a city.

    Args:
        city: City name, for example Paris.
    """
    return f"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.

Sources: Tool runner

Tool overheadTokens
Sonnet 5.5, Opus 5.5, Haiku 5.5 with auto or none286
Haiku 5.5 with any or tool406

Sources: Tool use overview

Server tools, MCP and citations

Search the web

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 query
        print("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 continue
    print("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.

Sources: Web search tool

  • 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.

Sources: MCP connector

Cite your documents

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 documents
            print(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.

Sources: Citations

Structured outputs

Get JSON that matches a schema

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()


class Contact(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.

Sources: Structured outputs

Thinking and effort

Control thinking depth

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.

Sources: Thinking · Effort

ModelDefaultTurn it off
Sonnet 5.5AdaptiveNot allowed. {"type": "between_tools"} is the lowest setting, accepted at effort high or below.
Opus 5.5, Fable 5.1Adaptive, always onNot allowed.
Haiku 5.5Adaptivedisabled works at effort high or below.
Opus 4.5, Sonnet 4.5, Haiku 4.5OffOnly 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.

A shelf of identical boxes where the first box glows and the glow runs along the row
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.

Sources: Prompt caching

Model5 minute write1 hour writeRead
Fable 5.1$12.50$20$0.25
Opus 5.5$5$8$0.20
Sonnet 5.5$2.50$4$0.10
Haiku 5.5, up to 100,000 tokens$0.125$0.20$0.01
  • 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 in enumerate(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 early

for 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 billed
        print(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.

Sources: Batch processing · Pricing

Files, images and PDFs

Python
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.

Sources: Files API · Vision · PDF support

Count tokens and work out the cost

Python
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:
    raise SystemExit("Prompt too long for the Haiku 5.5 low price band")
Python
import anthropic

# USD per million tokens: (input, output, cache-read multiplier). Checked Oct 28, 2026.
PRICES = {
    "claude-fable-5-1": (10.00, 50.00, 0.025),
    "claude-opus-5-5": (4.00, 20.00, 0.05),
    "claude-sonnet-5-5": (2.00, 10.00, 0.05),
    "claude-haiku-5-5": (0.10, 0.50, 0.10),  # 5x above 100,000 prompt tokens
}


def cost_usd(model, usage, batch=False):
    p_in, p_out, read_mult = PRICES[model]
    split = usage.cache_creation  # may be None
    w5 = split.ephemeral_5m_input_tokens if split else usage.cache_creation_input_tokens or 0
    w1 = split.ephemeral_1h_input_tokens if split else 0
    read = usage.cache_read_input_tokens or 0
    if model == "claude-haiku-5-5" and usage.input_tokens + read + w5 + w1 > 100_000:
        p_in, p_out = p_in * 5, p_out * 5
    total = usage.input_tokens * p_in + w5 * p_in * 1.25 + w1 * p_in * 2
    total += read * p_in * read_mult + usage.output_tokens * p_out
    total *= (0.5 if batch else 1) * (1.1 if usage.inference_geo == "us" else 1)
    return total / 1_000_000


client = anthropic.Anthropic()
model = "claude-sonnet-5-5"
msg = client.messages.create(
    model=model, max_tokens=256, messages=[{"role": "user", "content": "Say hi."}]
)
print(f"${cost_usd(model, msg.usage):.6f}")
Cost from response.usage: base input, cache writes and reads, output. Halve it for a batch.
  • client.messages.count_tokens(...) is free and has its own rate limit: 5,000, 10,000 or 20,000 requests a minute by tier. It returns an estimate.
  • Counting fails on server tools, the MCP connector and url or file sources in images and documents. Send base64 instead.
  • Batch halves the price. inference_geo="us" multiplies it by 1.1. Both stack with caching.
  • On Haiku 5.5, a prompt over 100,000 tokens is priced at 5x for the whole request, cache reads and writes included.
  • Priority Tier is not purchasable and not supported on the four current models, so service_tier changes nothing for them.

Sources: Token counting · Pricing · Data residency

Topic 5 of 6

Production

Errors, retries, rate limits and cloud platforms.

Errors, retries and rate limits

Python
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:  # 429
    print("slow down", e.response.headers.get("retry-after"), e.request_id)
except anthropic.OverloadedError as e:  # 529
    print("API overloaded", e.request_id)
except anthropic.APITimeoutError:  # subclass of APIConnectionError, so catch it first
    print("timed out")
except anthropic.APIConnectionError as e:
    print("network problem", e.__cause__)
except anthropic.APIStatusError as e:  # any other 4xx or 5xx
    print(e.status_code, e.request_id)
StatusExceptionMeaning
400BadRequestErrorThe request is malformed or uses something the model rejects.
401AuthenticationErrorThe key is wrong or missing.
403PermissionDeniedErrorThe key cannot use that resource.
404NotFoundErrorThe resource or model does not exist.
413RequestTooLargeErrorThe request is over the size limit.
429RateLimitErrorYou hit a rate limit or your spend cap.
500, 504InternalServerError, DeadlineExceededErrorSomething failed on the server side.
529OverloadedErrorThe 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.

Sources: API errors · Rate limits · Python SDK docs

Cloud platforms

Python
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)
PlatformPython clientModel ID for Opus 5.5
Amazon Bedrock, new projectsAnthropicBedrockMantleanthropic.claude-opus-5-5
Amazon Bedrock, InvokeModelAnthropicBedrockVaries by model, see the Bedrock page
Google Vertex AIAnthropicVertexclaude-opus-5-5
Microsoft FoundryAnthropicFoundryYour 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.

Sources: Claude in Amazon Bedrock · Claude on Google Cloud · Claude in Microsoft Foundry

Topic 6 of 6

Reference

The PDF, what changed this year, answers and sources.

Take it with you

PDF · 16 pages · 1.3 MB

Get the PDF

The whole sheet in one file: a cover, a poster of the eight defaults that bite, every table, all the recipes with their code, and the sources.

Enter your email and the PDF arrives in your inbox.

A fan of pages from the Claude API in Python cheat sheet

What changed in 2026

  1. Feb 17 to 19 · API

    Sonnet 4.6 launched. Automatic caching arrived as a single top level cache_control. Sonnet 3.7 and Haiku 3.5 retired. API release notes

  2. Mar 13 to 16 · API

    The 1M context window left beta for Opus 4.6 and Sonnet 4.6 at standard prices. The thinking.display field was added. API release notes

  3. Apr 16 · Opus 4.7

    Launched with API breaking changes and a new tokenizer that produces about 30 percent more tokens. API release notes

  4. Jun 9 · Fable 5

    Adaptive thinking only, and no prefill. Prefill returns a 400 from Claude 4.6 on. API release notes

  5. Jun 15 · Retirements

    Sonnet 4 and Opus 4 retired. Opus 4.1 followed on Aug 5. Model deprecations

  6. Aug 19 to 27 · Files API

    The Files API left beta, so client.files needs no beta header. API release notesFiles API

  7. Aug 20 · Python SDK 1.0.0

    Python 3.10 minimum, httpx2, Text Completions removed, temperature, top_p, top_k and output_format dicts removed. Python SDK changelogAPI release notes

  8. Sep 1 to Oct 7 · Fable 5.1 to Haiku 5.5

    Fable 5.1 (Sep 1), Opus 5.5 (Sep 22), Sonnet 5.5 (Sep 28) and Haiku 5.5 (Oct 7) launched. Models overview

  9. Sep 30 · Deprecation

    Sonnet 4.5 was deprecated. It retires on Nov 30, 2026. Model deprecations

  10. Oct 7 · Haiku 5.5

    Can reject code written for Haiku 4.5: budget_tokens, sampling parameters and assistant prefill all return 400s. API release notes

  11. Oct 15 · Fast mode

    Fast mode left research preview for Opus 5.5, with no beta header. API release notes

  12. Oct 21 · Python SDK 2.0.0

    API version 2026-10-21. Sampling parameters rejected, API key sent as a Bearer token, one credential per client, MessageParam became a union. Migrating to API version 2026-10-21Python SDK changelog

  13. Oct 22 to 23 · SDK 2.2 and 2.3

    Claude on Google Cloud launched with AnthropicGoogleCloud. Version 2.3.0 shipped on Oct 23. API release notesPython SDK changelog

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

Your AI agents, built by an AI engineer. No handoffs, no lock in. Start my agent build.