This Claude API Python tutorial takes you from an empty folder to a working app. You will set up a key, send messages, stream replies, call tools, read images and PDFs, cache prompts, run batches and handle errors.
Every code sample uses the official anthropic Python SDK and the current model IDs from Anthropic’s docs.
- Install:
pip install anthropic. The latest SDK is 2.6.0 (November 5, 2026) and needs Python 3.10+. - Key: create one in the Claude Console and set
ANTHROPIC_API_KEY. - Models:
claude-opus-5-5,claude-sonnet-5-5andclaude-haiku-5-5. All have a 1M token context window. - Core call:
client.messages.create(model=..., max_tokens=..., messages=[...]). - Save money: prompt caching cuts repeat input cost, and the Batch API is 50% off.
- Errors: the SDK retries 429, 5xx and connection errors twice by default.
Get an API Key and Install the SDK
- Create a Console account. Sign up at platform.claude.com and add billing or credits.
- Make a key. Open the API keys page in the Console and create a new key. Copy it now, because you cannot view it again.
- Create a project folder. Run
mkdir claude-app && cd claude-app, thenpython3 -m venv .venvand activate it. - Install the SDK. Run
pip install anthropic. - Set the key. On macOS or Linux run
export ANTHROPIC_API_KEY="your-api-key-here". In PowerShell use$env:ANTHROPIC_API_KEY = "your-api-key-here". - Test it. Save the first example below as
app.pyand runpython app.py.
Warning: never paste your key into code you commit or into front-end JavaScript. Keep it in an environment variable or a secrets manager. Anyone with the key can spend your credits.
Which Model ID to Use
These are the current models from Anthropic’s models overview. Prices are per million tokens (MTok).
| Model | API ID | Input / Output | Best for |
|---|---|---|---|
| Claude Opus 5.5 | claude-opus-5-5 |
$4 / $20 | Anthropic’s suggested start for most work, agents and coding |
| Claude Sonnet 5.5 | claude-sonnet-5-5 |
$2 / $10 | Best mix of speed and intelligence |
| Claude Haiku 5.5 | claude-haiku-5-5 |
From $0.10 / $0.50 | High-volume, low-latency jobs like classification |
| Claude Fable 5.1 | claude-fable-5-1 |
$10 / $50 | Hardest reasoning and long agent runs |
All four have a 1M token context window and up to 128K output tokens. Full pricing, including cache and batch rates, is on our Claude API pricing page. For the small model, see the Claude Haiku 5.5 guide.
Send Your First Message
The Messages API is the main endpoint. You send a list of messages and get back a list of content blocks.
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain recursion in two sentences."}],
)
for block in message.content:
if block.type == "text":
print(block.text)
print(message.usage) # input_tokens, output_tokens, cache fields
model: one of the IDs above.max_tokens: the most tokens Claude may write. It is required.messages: a list ofuserandassistantturns.stop_reason: on the response, such asend_turn,max_tokensortool_use.
For async code, use AsyncAnthropic and await client.messages.create(...). The arguments are the same.
System Prompts and Multi-Turn Chats
A system prompt sets Claude’s role and rules. It goes in the top-level system field, not inside messages.
history = []
def chat(user_text):
history.append({"role": "user", "content": user_text})
reply = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
system="You are a friendly Python tutor. Keep answers under 120 words.",
messages=history,
)
text = "".join(b.text for b in reply.content if b.type == "text")
history.append({"role": "assistant", "content": reply.content})
return text
print(chat("What is a list comprehension?"))
print(chat("Show me one with a condition."))
The API is stateless. You send the whole history each turn, which is why caching and message threads matter for long chats.
Streaming Responses
Streaming prints text as it is generated, so users see the reply start at once. The SDK’s messages.stream() helper is the easiest way.
with client.messages.stream(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a haiku about Python."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print()
print(final.usage)
Note: the SDK raises a ValueError if a non-streaming request is expected to take longer than about 10 minutes. For big max_tokens values, always stream.
Tool Use (Function Calling)
Tool use lets Claude ask your code to run a function. You describe the tool with a JSON schema, Claude returns a tool_use block, you run it, and you send back a tool_result.
import json
tools = [{
"name": "get_weather",
"description": "Get the current weather for a given location.",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string", "description": "City and state"}},
"required": ["location"],
},
}]
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024,
tools=tools, messages=messages)
while resp.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": resp.content})
results = []
for block in resp.content:
if block.type == "tool_use":
output = json.dumps({"temp_c": 15, "sky": "partly cloudy"}) # your real lookup
results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
messages.append({"role": "user", "content": results})
resp = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024,
tools=tools, messages=messages)
print(next(b.text for b in resp.content if b.type == "text"))
Let the SDK run the loop
The SDK also has a beta tool runner. Decorate a normal function with @beta_tool, pass it to client.beta.messages.tool_runner(...), and loop over the runner. It calls your tools and sends results back for you.
Anthropic also offers server tools that run on its side, such as web search, which you add with one entry in tools.
Vision: Send Images
Claude can read JPEG, PNG, GIF and WebP images. Send them as a URL, as base64 or as an uploaded file ID.
import base64
with open("chart.png", "rb") as f:
img_b64 = base64.standard_b64encode(f.read()).decode("utf-8")
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img_b64}},
{"type": "text", "text": "What trend does this chart show?"},
],
}],
)
print(next(b.text for b in message.content if b.type == "text"))
For a public image, swap the source for {"type": "url", "url": "https://..."}. Put the image before your question for the best results.
Read PDFs
PDFs use a document block. Claude reads both the text and the visual parts, such as charts and tables.
with open("report.pdf", "rb") as f:
pdf_b64 = base64.standard_b64encode(f.read()).decode("utf-8")
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=2048,
messages=[{
"role": "user",
"content": [
{"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_b64}},
{"type": "text", "text": "Summarize the key findings in 5 bullets."},
],
}],
)
print(next(b.text for b in message.content if b.type == "text"))
You can also pass {"type": "url", "url": ...}, or upload once with client.files.upload(...) and reuse the file_id. Pages count as tokens, so long PDFs cost more.
Prompt Caching
Caching stores the start of your prompt, such as a long system prompt or document, so later calls read it cheaply. The simplest way is automatic caching with one top-level field.
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
cache_control={"type": "ephemeral"}, # automatic caching
system=LONG_SYSTEM_PROMPT,
messages=[{"role": "user", "content": "Answer using the policy above."}],
)
print(response.usage.cache_creation_input_tokens, response.usage.cache_read_input_tokens)
- Lifetime: 5 minutes by default, refreshed each time the cache is used. A 1-hour TTL costs more.
- Write cost: 1.25x the base input price for the 5-minute cache.
- Read cost: 0.1x base input on most models, and 0.05x on Opus 5.5 and Sonnet 5.5.
- Minimum size: 512 tokens on the current models. Shorter prompts are not cached and no error is returned.
For a deeper dive, read our prompt caching guide.
Message Threads (Beta)
Message threads keep a conversation on Anthropic’s servers between turns. After the first call, you send only the new message plus the ID of the last response.
thread = client.beta.messages.thread(
model="claude-sonnet-5-5",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system="You are a support agent for Acme. Keep replies short.",
)
thread.send({"role": "user", "content": "Hi! I have a question about an order."})
second = thread.send({"role": "user", "content": "How long does standard shipping take?"})
print(next(b.text for b in second.content if b.type == "text"))
The helper uses the message-threads-2026-08-12 beta. A thread expires 5 minutes after its last request, and a continue call then returns a 404 with thread_not_found, so you replay the full chat. Our message threads guide covers the raw request format.
Batch Processing
The Message Batches API runs many requests in the background at 50% off. It suits evaluations, bulk tagging and overnight jobs.
import time
batch = client.messages.batches.create(requests=[
{"custom_id": f"review-{i}",
"params": {"model": "claude-haiku-5-5", "max_tokens": 200,
"messages": [{"role": "user", "content": f"Classify the sentiment: {text}"}]}}
for i, text in enumerate(["Love it!", "Too slow.", "It's fine."])
])
while client.messages.batches.retrieve(batch.id).processing_status != "ended":
time.sleep(60)
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
print(result.custom_id, next(b.text for b in result.result.message.content if b.type == "text"))
- Size: up to 100,000 requests or 256 MB per batch.
- Speed: most batches finish within 1 hour. A batch expires if it is not done in 24 hours.
- Results: available for 29 days after the batch is created.
Error Handling and Retries
The SDK raises typed exceptions, so you can handle each case cleanly.
import anthropic
client = anthropic.Anthropic(max_retries=4, timeout=60.0)
try:
msg = client.messages.create(model="claude-sonnet-5-5", max_tokens=512,
messages=[{"role": "user", "content": "Hello"}])
except anthropic.RateLimitError:
print("429: slow down and retry later")
except anthropic.APIConnectionError as e:
print("Network problem:", e.__cause__)
except anthropic.APIStatusError as e:
print("API error", e.status_code, e.response)
| Status | SDK exception | Usual fix |
|---|---|---|
| 400 | BadRequestError |
Check the request shape and model ID |
| 401 | AuthenticationError |
Check ANTHROPIC_API_KEY |
| 403 | PermissionDeniedError |
Your key lacks access to that resource |
| 404 | NotFoundError |
Wrong model ID, or an expired thread |
| 429 | RateLimitError |
Back off and retry, or raise your tier |
| 529 / 5xx | InternalServerError |
Overloaded or server error: retry with backoff |
By default the SDK retries connection errors, 408, 409, 429 and 5xx twice with exponential backoff, and requests time out after 10 minutes. Log message._request_id so Anthropic support can trace a failing call. More fixes are in our 429 and 529 errors guide.
Putting It Together: A Production Checklist
Once the basics work, a few habits keep a Claude app fast, cheap and reliable. Use this list before you ship.
- Pick the smallest model that passes: test your prompts on Haiku 5.5 or Sonnet 5.5 first, and move up to Opus 5.5 only where quality needs it.
- Stream anything user-facing: people see the first words right away instead of waiting for the whole reply.
- Cache the stable part: put long system prompts, tool lists and documents first, then add
cache_control. - Batch what can wait: nightly reports, data cleanup and evaluations cost half as much through the Batch API.
- Cap output: set a sensible
max_tokensfor each task so one runaway reply cannot drain your budget. - Log the basics: store
_request_id, the model,stop_reasonandusagefor every call. - Plan for failure: keep SDK retries on, and show a friendly message when a 429 or 529 lasts longer than your retries.
- Protect the key: call the API only from your server, and use separate keys for development and production.
These habits head off the most common surprise bills and outages.
Troubleshooting: Common Problems and Fixes
- “Could not resolve authentication method”: the key is not set in the shell that runs Python. Export it again or pass
api_key=to the client. - 404 not_found_error on the model: the model ID has a typo. Use the exact IDs, such as
claude-sonnet-5-5. - Reply is cut off:
stop_reasonismax_tokens. Raisemax_tokensand stream long outputs. - Cache never hits: the prompt is under the minimum size, or something before the cached part changes each call, such as a timestamp.
- Tool loop never ends: make sure every
tool_useblock gets a matchingtool_resultwith the sametool_use_id. - Image or PDF rejected: check the
media_typematches the file, and that base64 is a plain string with no data URL prefix. - Costs higher than expected: check
usageon each response, and track spend on the Console usage pages.
Frequently Asked Questions
How do I install the Claude API for Python?
Run pip install anthropic on Python 3.10 or newer. Then set ANTHROPIC_API_KEY and create a client with anthropic.Anthropic().
Is the Claude API free?
No. You pay per token, with prices that depend on the model. A Claude.ai Pro or Max plan does not include API usage.
What is the model ID for Claude Sonnet 5.5?
It is claude-sonnet-5-5. Opus 5.5 is claude-opus-5-5 and Haiku 5.5 is claude-haiku-5-5.
How do I stream Claude’s answer in Python?
Use with client.messages.stream(...) as stream: and loop over stream.text_stream. Call stream.get_final_message() at the end for usage data.
Can Claude read PDFs through the API?
Yes. Send a document block with a base64 PDF, a URL or an uploaded file ID. Claude reads text, tables and charts.
How much does batch processing save?
The Message Batches API costs 50% less than standard calls. Most batches finish within an hour, with a 24-hour limit.
Does the Python SDK retry failed requests?
Yes. It retries connection errors, 408, 409, 429 and 5xx errors twice by default. Change this with max_retries.
Sources
- Claude Docs: Get started
- Claude Docs: Models overview
- Claude Docs: Python SDK
- Claude Docs: Streaming Messages
- Claude Docs: Tool use
- Claude Docs: Vision
- Claude Docs: PDF support
- Claude Docs: Prompt caching
- Claude Docs: Message threads
- Claude Docs: Batch processing
- Claude Docs: Errors
- anthropic on PyPI



