> ## Content Index
> Fetch the complete content index at: https://bullquill.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Claude API Python tutorial: from first request to tool use
- URL: https://bullquill.com/how-to-use-claude-api-python/
- Published: 2026-10-27T00:15:03.000Z
- Updated: 2026-10-27T00:26:27.000Z
- Description: 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.
- Author: Rowan Ashby
- Tags: AI, Tutorial, How-To, Anthropic, Claude, Python, #lang-en, #evergreen, #tr-how-to-use-claude-api-python

## Key takeaways

- Install the official SDK with `pip install anthropic` (Python 3.10 or later), export `ANTHROPIC_API_KEY`, and every call goes through `client.messages.create()`.
- Current model IDs are `claude-opus-5-5`, `claude-sonnet-5-5`, `claude-haiku-5-5` and `claude-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 `system` parameter.
- Version 2.0.0 of the SDK uses API version `2026-10-21`, which rejects `temperature`, `top_p` and `top_k` with a 400 error.
- Tool use is a loop: Claude returns `stop_reason: "tool_use"`, your code runs the function, and you reply with a `tool_result` block.

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](https://platform.claude.com/?ref=bullquill.com), 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](https://bullquill.com/prompt-engineering-techniques-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](https://bullquill.com/how-to-cut-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 Anthropic
```

Order 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](https://bullquill.com/how-to-build-ai-agent-tool-calling-python/). If you want reusable tools that work across apps, read [how to build an MCP server in Python](https://bullquill.com/how-to-build-mcp-server-python/), and for typed results without tools, see [structured JSON output from an LLM in Python](https://bullquill.com/how-to-get-structured-json-output-llm-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 version `2026-10-21` this 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 with `stop_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.