AIAITutorial NO. ---
Claude API Python tutorial: from first request to tool use
Install Anthropic's official Python SDK, pick a current model ID such as claude-sonnet-5-5, then build up to streaming, chat memory, error handling and a working tool call, checked against API version 2026-10-21.
Key takeaways
- Install the official SDK with
pip install anthropic(Python 3.10 or later), exportANTHROPIC_API_KEY, and every call goes throughclient.messages.create(). - Current model IDs are
claude-opus-5-5,claude-sonnet-5-5,claude-haiku-5-5andclaude-fable-5-1; all four have a 1M-token context window and up to 128K output tokens, according to Anthropic's models overview. - The API is stateless: you send the whole conversation each time, and put standing instructions in the top-level
systemparameter. - Version 2.0.0 of the SDK uses API version
2026-10-21, which rejectstemperature,top_pandtop_kwith a 400 error. - Tool use is a loop: Claude returns
stop_reason: "tool_use", your code runs the function, and you reply with atool_resultblock.
To use the Claude API in Python, you need three things: an API key from the Claude Console, the anthropic package, and about ten lines of code. This guide, checked against Anthropic's documentation on 27 October 2026, covers streaming, chat memory, error handling and tool use.
Each step builds on the previous one, so you can stop at the level you need. The code targets the current 2.x SDK; where older tutorials will now break, the difference is pointed out.
Step 1: Get a Claude API key
Sign in to the Claude Console, open the API keys page and create a key. The API is billed separately from a claude.ai chat subscription, so you will need to add credit or billing details before requests succeed.
Treat the key like a password. Do not paste it into source files or commit it to Git. The SDK reads it automatically from an environment variable:
# macOS / Linux
export ANTHROPIC_API_KEY="your-api-key-here"
# Windows PowerShell
$env:ANTHROPIC_API_KEY = "your-api-key-here"Anthropic's SDK documentation also suggests python-dotenv if you prefer a .env file; just make sure that file is listed in .gitignore.
Step 2: Install the anthropic Python SDK
The official package is called anthropic and requires Python 3.10 or later. Install it in a virtual environment so it does not clash with other projects:
mkdir claude-quickstart && cd claude-quickstart
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade anthropic
python -c "import anthropic; print(anthropic.__version__)"If the version printed starts with 0. or 1., you are on an older major release. Anthropic's migration guide states that the Python SDK's 2.0.0 release is the first to send API version 2026-10-21; older releases send 2023-06-01.
Step 3: Make your first Claude API call in Python
Create quickstart.py:
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Explain what an API key is in two sentences."}
],
)
for block in message.content:
if block.type == "text":
print(block.text)
print(message.stop_reason, message.usage)Run it with python quickstart.py. Three parameters are required on every request: model, max_tokens (the ceiling on the reply length) and messages.
The reply is a Message object, not a string. Its content is a list of blocks, which is why the loop checks block.type. Current models think adaptively, and responses can contain block types other than text, so reading message.content[0].text blindly is fragile. The stop_reason field tells you why the reply ended: end_turn for a natural finish, max_tokens if it hit your limit, tool_use when Claude wants to call a tool, and refusal when it declines.
Which Claude model ID should you use?
Anthropic's models overview recommends starting with Claude Opus 5.5 for most workloads. The table lists the four current models and their list prices as published on Anthropic's pricing page on 27 October 2026.
| Model | API model ID | Input (per 1M tokens) | Output (per 1M tokens) | Best for (per Anthropic) |
|---|---|---|---|---|
| Claude Fable 5.1 | claude-fable-5-1 | $10 | $50 | Demanding reasoning, long-horizon agents |
| Claude Opus 5.5 | claude-opus-5-5 | $4 | $20 | Default choice; agentic coding and knowledge work |
| Claude Sonnet 5.5 | claude-sonnet-5-5 | $2 | $10 | Balance of speed and intelligence |
| Claude Haiku 5.5 | claude-haiku-5-5 | $0.10 (prompts up to 100K tokens) | $0.50 (prompts up to 100K tokens) | High-volume classification, extraction, routing |
Source: Anthropic models overview and pricing pages, data as of 27 October 2026 (UTC). Haiku 5.5 costs $0.50 input and $2.50 output per million tokens for prompts over 100,000 tokens. The Batch API halves all of these prices. All four models share a 1M-token context window and a 128K-token maximum output on the synchronous Messages API.
From the 4.6 generation onward, Anthropic says every model ID is a pinned snapshot, so claude-opus-5-5 will not silently change under you. A 1M-token context window means the prompt, the conversation history and the reply must together fit within that limit.
Step 4: Add a system prompt
The system prompt sets Claude's role, tone and rules for the whole conversation. Pass it as the top-level system parameter, not as a message:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
system=(
"You are a code reviewer for a Python team. "
"Answer in bullet points and flag security issues first."
),
messages=[
{"role": "user", "content": "Review: password = input(); print(password)"}
],
)Newer models, including Opus 5.5, Sonnet 5.5 and Haiku 5.5, also accept messages with "role": "system" placed after a user turn, to add an instruction partway through a chat. Anthropic's documentation says such a message cannot be the first entry in messages, so instructions that apply from the start still belong in the top-level system field.
Two habits from older tutorials no longer work. Prefilling the start of Claude's answer with a final assistant message returns a 400 error on Claude 4.6 and later models. And under API version 2026-10-21, sending temperature, top_p or top_k returns a 400 error even at default values. Steer the output with instructions in the prompt instead; our prompt engineering guide covers the techniques that work.
Step 5: Stream the response token by token
For chat interfaces, and for any long reply, streaming shows text as it is generated instead of after the whole answer is finished. The SDK's helper wraps this in a context manager:
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-5-5",
max_tokens=2048,
messages=[{"role": "user", "content": "Write a haiku about Python decorators."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print()
final = stream.get_final_message()
print(final.usage)get_final_message() returns the same assembled Message object that a normal call would, including token usage. If you only need raw events and want to save memory, pass stream=True to messages.create() and iterate over the events yourself.
Anthropic's SDK documentation advises streaming for long requests: the client raises a ValueError if a non-streaming request is expected to take longer than about ten minutes, and some networks drop idle connections before then.
Step 6: Build a multi-turn chat with conversation history
The Messages API is stateless. Claude does not remember earlier requests, so your code must keep the history and send all of it every time. Messages alternate between user and assistant:
import anthropic
client = anthropic.Anthropic()
history = []
while True:
user_text = input("You: ").strip()
if user_text.lower() in {"quit", "exit"}:
break
history.append({"role": "user", "content": user_text})
with client.messages.stream(
model="claude-opus-5-5",
max_tokens=1024,
system="You are a friendly tutor for beginner Python programmers.",
messages=history,
) as stream:
print("Claude: ", end="")
for text in stream.text_stream:
print(text, end="", flush=True)
print()
reply = stream.get_final_message()
# Append the full content list so any non-text blocks are kept
history.append({"role": "assistant", "content": reply.content})Because the full history is re-sent on each turn, input tokens grow with every exchange. For long sessions, trim or summarise old turns, or use prompt caching: on Opus 5.5 and Sonnet 5.5, Anthropic prices cache hits at 5% of the base input rate. Our guide to cutting LLM API costs explains caching, batching and model routing in more detail.
Step 7: Handle errors, retries and timeouts
The SDK raises a subclass of anthropic.APIError whenever a request fails. Catch the specific ones you can act on:
import anthropic
client = anthropic.Anthropic(max_retries=4, timeout=60.0)
try:
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
except anthropic.APIConnectionError as e:
print("Network problem, the API could not be reached:", e.__cause__)
except anthropic.RateLimitError:
print("429: too many requests, slow down and retry later.")
except anthropic.APIStatusError as e:
print("API returned an error:", e.status_code, e.response)
else:
print(message._request_id) # log this when reporting problems to AnthropicOrder matters: RateLimitError is itself an APIStatusError, so the more specific class must come first.
| HTTP status | SDK exception | Usual cause |
|---|---|---|
| 400 | BadRequestError | Invalid request, e.g. a removed parameter such as temperature |
| 401 | AuthenticationError | Missing or wrong API key |
| 403 | PermissionDeniedError | Key lacks access to the resource |
| 404 | NotFoundError | Mistyped model ID or endpoint |
| 429 | RateLimitError | Rate limit for your usage tier reached |
| 500 and above | InternalServerError | Server-side problem |
| None | APIConnectionError | Network failure; APITimeoutError on timeout |
Source: Anthropic Python SDK documentation. By default the SDK retries connection errors, 408, 409, 429 and 5xx responses twice with exponential backoff, and times out after ten minutes. Change both with max_retries and timeout, on the client or per request via client.with_options().
Step 8: Count tokens and estimate cost
Every response reports its usage, and you can count a prompt's tokens before sending it:
count = client.messages.count_tokens(
model="claude-opus-5-5",
messages=[{"role": "user", "content": "Summarise this report..."}],
)
print("Prompt tokens:", count.input_tokens)
# After a real call:
u = message.usage
cost = u.input_tokens / 1_000_000 * 4 + u.output_tokens / 1_000_000 * 20
print(f"Approximate Opus 5.5 cost: ${cost:.6f}")The prices in that calculation are Opus 5.5's list rates of $4 per million input tokens and $20 per million output tokens. Swap in the rates for whichever model you use, and note that cache writes, cache reads and batch requests are billed differently.
Step 9: Let Claude call your Python functions (tool use)
Tool use, also called function calling, lets Claude ask your program to run a function and then use the result in its answer. You describe each tool with a name, a description and a JSON Schema input_schema. When Claude wants a tool, the response ends with stop_reason set to "tool_use"; you run the function and send back a tool_result block that carries the matching tool_use_id.
import json
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-opus-5-5"
def get_weather(location):
# Replace with a call to a real weather API
return json.dumps({"location": location, "temp_c": 15, "condition": "light rain"})
TOOLS = [
{
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name, e.g. Paris"}
},
"required": ["location"],
},
}
]
messages = [{"role": "user", "content": "Do I need an umbrella in Paris today?"}]
while True:
response = client.messages.create(
model=MODEL, max_tokens=1024, tools=TOOLS, messages=messages
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = []
for block in response.content:
if block.type == "tool_use" and block.name == "get_weather":
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": get_weather(**block.input),
})
messages.append({"role": "user", "content": results})
print("".join(b.text for b in response.content if b.type == "text"))The loop matters: Claude may call several tools, or call one tool more than once, before it gives a final answer. If a tool fails, return the error message in the tool_result with "is_error": True so Claude can recover instead of guessing.
Two refinements are worth knowing. Adding "strict": True to a tool definition makes Claude's tool inputs match your schema exactly, per Anthropic's tool-use documentation. And the SDK's @beta_tool decorator with client.beta.messages.tool_runner() builds the schema from a function's signature and docstring and runs this loop for you.
Tools add cost: the definitions count as input tokens, and Anthropic adds a hidden tool-use system prompt of 286 tokens on Opus 5.5, Sonnet 5.5 and Haiku 5.5 when tool_choice is auto. For a deeper, provider-neutral walk-through of agent loops, see how to build an AI agent with tool calling in Python. If you want reusable tools that work across apps, read how to build an MCP server in Python, and for typed results without tools, see structured JSON output from an LLM in Python.
Common mistakes when using the Claude API in Python
- Copying old model IDs. Tutorials from 2024 and 2025 often use IDs for models that are now legacy or retired. Use a current ID from the table above or query the Models API.
- Sending
temperature. Under API version2026-10-21this returns a 400 error. Remove the parameter rather than setting it to 1.0. - Forgetting
max_tokens. It is required, and a reply that stops withstop_reason == "max_tokens"has been cut off. - Dropping assistant turns. If you only store user messages, Claude loses the thread. Append each full reply to the history.
- Hard-coding the key. Keys pasted into notebooks and scripts end up in Git history. Keep them in environment variables or a secrets manager.
What it means for developers
The core of the Claude API in Python fits on one screen: create a client, call messages.create(), read the content blocks. Everything else in this guide (system prompts, streaming, history, error handling, tools) is a small addition to that one call, which makes the SDK easy to adopt in an existing codebase.
The bigger practical change this autumn is the move to API version 2026-10-21 in SDK 2.0.0. Code that still sets sampling parameters or prefills assistant replies will fail after upgrading, so check those two patterns first when you update a project. Pin the SDK version in requirements.txt, pin a model ID, and log _request_id on errors, and your integration will be predictable as Anthropic ships new models.
Frequently asked questions
How do I get a Claude API key?
Create an account in the Claude Console at platform.claude.com, open the API keys page and generate a key. Store it in the ANTHROPIC_API_KEY environment variable; the official Python SDK reads it automatically.
Which Python package do I need for the Claude API?
Anthropic's official SDK, installed with pip install anthropic. It requires Python 3.10 or later, and version 2.0.0 is the first release that uses API version 2026-10-21.
Which Claude model ID should I use in Python?
Anthropic recommends starting with claude-opus-5-5 for most workloads. claude-sonnet-5-5 is faster and cheaper, claude-haiku-5-5 suits high-volume simple tasks, and claude-fable-5-1 is for the most demanding reasoning.
Why does the Claude API return a 400 error for temperature?
API version 2026-10-21, used by SDK 2.0.0, removed the temperature, top_p and top_k sampling parameters. Any request that sends them returns a 400 error, so remove them and steer output through the prompt.