Claude API Python Tutorial: Complete Guide 2026

Share on SNS

The Claude API Python tutorial most developers need in 2026 is shorter than you’d expect — your first working API call is 10 lines of code. The part that takes longer is understanding the response object, getting system prompts right, handling the streaming correctly, and avoiding the three parameter changes in Claude Sonnet 5 that silently break code written for Sonnet 4.x.

claude api python tutorial complete guide 2026

This guide covers all of it. By the end you’ll have working code for every major Claude API pattern: basic calls, multi-turn conversations, streaming, tool use, and production error handling with cost tracking. Everything runs against claude-sonnet-5 — the current production default at $2 per million input tokens through August 31, 2026.


Setup: API Key and SDK in 2 Minutes

Step 1 — Get your API key

Go to console.anthropic.com, create an account, and generate an API key under API Keys. New accounts include free credits. Copy the key once — the console doesn’t show it again.

Step 2 — Install the SDK

pip install anthropic python-dotenv

Step 3 — Store the key safely

# .env file in your project root — never commit this to git
ANTHROPIC_API_KEY=sk-ant-your-key-here
# .gitignore
.env

Your First Claude API Python Call

import os
import anthropic
from dotenv import load_dotenv

load_dotenv()

client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Explain what an API is in two sentences."}
    ]
)

print(message.content[0].text)

Run this and you’ll see Claude’s response in your terminal. That’s the complete minimal Claude API Python call. Now let’s understand what came back.

Reading the response object correctly

print(message.model)               # "claude-sonnet-5"
print(message.stop_reason)         # "end_turn" = completed normally
print(message.usage.input_tokens)  # tokens you sent (= your cost)
print(message.usage.output_tokens) # tokens in the response (= your cost)
print(message.content[0].text)     # the actual response text
print(message.content[0].type)     # "text" for standard responses

Two fields matter most: stop_reason tells you why Claude stopped — end_turn means it finished normally, max_tokens means it was cut off and you need a higher limit. usage is how you track costs — essential before you put anything into production.


System Prompts: The Most Important Parameter

A system prompt sets Claude’s role, constraints, and output format for the entire conversation. It’s the highest-leverage single parameter in any Claude API Python integration — the difference between a generic response and a response that matches your product exactly.

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    system="""You are a senior Python engineer doing code review.
When reviewing code:
- Start with a one-line verdict: APPROVE, REQUEST_CHANGES, or NEEDS_DISCUSSION
- List specific issues with line numbers
- End with the single highest-priority fix
Keep your review under 200 words.""",
    messages=[
        {"role": "user", "content": "Review this code:\n\ndef get_user(id):\n    return db.query(f'SELECT * FROM users WHERE id={id}')"}
    ]
)

print(message.content[0].text)

Three system prompt patterns that consistently produce better Claude API responses:

  1. Define a role with context: “You are a [role] with [specific expertise]” gives Claude a consistent perspective to respond from.
  2. Specify the output format: Telling Claude exactly what structure you expect (bullet points, JSON, numbered steps) makes the output far easier to parse.
  3. Set explicit constraints: Word limits, what to include, what to exclude. Claude follows precise constraints reliably.

Multi-Turn Conversations

Claude doesn’t have memory between API calls. To maintain a conversation, you pass the full history with every request. The SDK doesn’t manage this for you — you build the list yourself.

import os
import anthropic
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

def chat():
    """A simple multi-turn CLI chatbot using the Claude API."""
    history = []
    print("Claude — type 'quit' to exit\n")

    while True:
        user_input = input("You: ").strip()
        if user_input.lower() in ("quit", "exit", "q"):
            break
        if not user_input:
            continue

        # Add user message to history
        history.append({"role": "user", "content": user_input})

        response = client.messages.create(
            model="claude-sonnet-5",
            max_tokens=1024,
            system="You are a helpful assistant. Be concise.",
            messages=history
        )

        assistant_text = response.content[0].text

        # Add Claude's response to history for the next turn
        history.append({"role": "assistant", "content": assistant_text})

        print(f"Claude: {assistant_text}\n")

        # Track token usage per turn
        print(f"[Tokens: in={response.usage.input_tokens} out={response.usage.output_tokens}]")

if __name__ == "__main__":
    chat()

The pattern is always the same: append the user message, make the API call, append Claude’s response to the same list, repeat. The context grows with each turn — which is why token tracking per turn matters. A 10-turn conversation with 500 tokens per response is using 5,000 output tokens before you’ve done anything interesting.


Streaming: Token-by-Token Output

Streaming makes long responses feel fast by printing each token as it’s generated rather than waiting for the full response. For any user-facing application with responses longer than a sentence, streaming is the default choice.

import os
import anthropic
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

def stream_response(prompt: str) -> str:
    """Stream Claude's response token by token, return the full text."""
    full_response = ""

    with client.messages.stream(
        model="claude-sonnet-5",
        max_tokens=2048,
        messages=[{"role": "user", "content": prompt}]
    ) as stream:
        for text in stream.text_stream:
            print(text, end="", flush=True)
            full_response += text

    print()  # newline after streaming completes
    return full_response


# Usage
result = stream_response(
    "Write a step-by-step explanation of how HTTPS works."
)
print(f"\n[Total characters: {len(result)}]")

The stream.text_stream iterator yields only the text tokens, automatically filtering out thinking blocks if extended thinking is active. For non-streaming use cases (batch processing, background jobs), omit the stream context manager and use client.messages.create() as shown earlier.


The Three Sonnet 5 Breaking Changes From Sonnet 4.x

If you’ve used the Claude API with Sonnet 4.6 or earlier and are migrating to Sonnet 5, three parameter changes produce silent failures or 400 errors:

  1. Do not pass temperature. Sonnet 5 uses an effort-level system instead of temperature. Passing temperature to a Sonnet 5 call returns a 400 error. Remove it entirely or replace it with the effort toggle (see below).
  2. Effort toggle, not temperature: To control reasoning intensity, pass the thinking parameter with a budget: thinking={"type": "enabled", "budget_tokens": 5000} for extended reasoning. For most tasks, omit this entirely — the default reasoning level is appropriate.
  3. Model string: Use claude-sonnet-5. The claude-sonnet-4-6 string still works for that model — but if you’re migrating, the strings are not interchangeable aliases.
# WRONG — returns 400 error with claude-sonnet-5
response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    temperature=0.7,  # ← causes 400 error
    messages=[{"role": "user", "content": "Hello"}]
)

# CORRECT — omit temperature entirely
response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}]
)

# CORRECT — use thinking param for high-reasoning tasks
response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=8000,
    thinking={"type": "enabled", "budget_tokens": 5000},
    messages=[{"role": "user", "content": "Solve this step by step: ..."}]
)

Production Error Handling and Cost Tracking

Two additions that every Claude API Python integration needs before it sees production traffic: structured error handling and per-call cost tracking.

import os
import anthropic
from dotenv import load_dotenv
from dataclasses import dataclass

load_dotenv()
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

# Sonnet 5 pricing (introductory, valid through August 31, 2026)
PRICE_INPUT_PER_MTK = 2.0   # $2.00 per million input tokens
PRICE_OUTPUT_PER_MTK = 10.0  # $10.00 per million output tokens

@dataclass
class APIResult:
    text: str
    input_tokens: int
    output_tokens: int
    cost_usd: float
    stop_reason: str


def call_claude(
    prompt: str,
    system: str = "",
    max_tokens: int = 1024
) -> APIResult | None:
    """
    Call Claude API with full error handling and cost tracking.
    Returns None on failure — always check the return value.
    """
    try:
        kwargs = {
            "model": "claude-sonnet-5",
            "max_tokens": max_tokens,
            "messages": [{"role": "user", "content": prompt}]
        }
        if system:
            kwargs["system"] = system

        response = client.messages.create(**kwargs)

        # Extract text safely
        text = ""
        for block in response.content:
            if hasattr(block, "text"):
                text += block.text

        # Calculate cost
        input_cost = (response.usage.input_tokens / 1_000_000) * PRICE_INPUT_PER_MTK
        output_cost = (response.usage.output_tokens / 1_000_000) * PRICE_OUTPUT_PER_MTK
        total_cost = input_cost + output_cost

        return APIResult(
            text=text,
            input_tokens=response.usage.input_tokens,
            output_tokens=response.usage.output_tokens,
            cost_usd=total_cost,
            stop_reason=response.stop_reason
        )

    except anthropic.AuthenticationError:
        print("ERROR: Invalid API key. Check your ANTHROPIC_API_KEY.")
        return None

    except anthropic.RateLimitError:
        print("ERROR: Rate limit exceeded. Add retry logic or reduce request rate.")
        return None

    except anthropic.APIStatusError as e:
        print(f"ERROR: API error {e.status_code}: {e.message}")
        return None

    except Exception as e:
        print(f"ERROR: Unexpected error: {e}")
        return None


# Usage
result = call_claude(
    prompt="What are the top 3 Python best practices for API integrations?",
    system="You are a senior Python developer. Be specific and concise."
)

if result:
    print(result.text)
    print(f"\n--- Usage ---")
    print(f"Tokens: {result.input_tokens} in / {result.output_tokens} out")
    print(f"Cost: ${result.cost_usd:.6f}")
    print(f"Stop reason: {result.stop_reason}")
else:
    print("Call failed — check the error above.")

The APIResult dataclass makes it easy to log costs to a database, aggregate them per user or per session, and alert when a single call exceeds a cost threshold. Running this in production without cost tracking is how teams discover they’ve spent $200 on a single batch job.

For the complete Claude API reference, authentication options, and SDK changelog, see Anthropic’s official API documentation.


The Builder’s Takeaway

The Claude API Python integration pattern is: install the SDK, initialize the client with your key, build a messages list, call client.messages.create(), and read response.content[0].text. Everything else — system prompts, multi-turn history, streaming, error handling, cost tracking — layers onto that foundation. The three Sonnet 5 breaking changes (no temperature, effort toggle, new model string) are the only migration friction from 4.x. Once past those, Sonnet 5’s 1M context window, adaptive reasoning, and introductory pricing make it the right default for almost any Python application adding Claude in 2026. The next step after this guide is adding tool use — which turns the Claude API from a text generator into an agent that can take actions in your codebase and beyond.


Continue in This Series


This post is part of The Agentic Protocol’s Work series — the connective infrastructure layer beneath every autonomous pipeline. See also: How to Build an AI Agent With Python.


Share on SNS