Skip to content

Anthropic API (Claude)

Claude is Anthropic's LLM — known for strong reasoning, long context (200K tokens), and safety. The API follows a similar pattern to OpenAI but with key differences: system is a top-level parameter (not a message), extended thinking is built-in, and tool use is deeply integrated.

Table of Contents

Installation / Setup

pip install anthropic                     # or: uv add anthropic
export ANTHROPIC_API_KEY="YOUR_API_KEY"   # never commit real keys
import anthropic
client = anthropic.Anthropic(api_key="YOUR_API_KEY")

Core Syntax

Messages (the core endpoint)

response = client.messages.create(
    model="claude-sonnet-4-20250514",       # or claude-haiku-4-20250414, claude-opus-4-20250514
    max_tokens=1024,
    system="You are a helpful data science tutor.",  # NOTE: system is a top-level param
    messages=[
        {"role": "user", "content": "Explain bias-variance trade-off."},
    ],
)
print(response.content[0].text)
print(response.usage)                        # input_tokens, output_tokens

Key difference from OpenAI: system is a separate parameter, not a message role.

Models available

Model Context Best for Cost
claude-haiku-4-20250414 200K Fast, cheap, classification $
claude-sonnet-4-20250514 200K Balanced — good for most tasks $$
claude-opus-4-20250514 200K Best reasoning, complex analysis $$$$

Messages format

# system — top-level parameter (not in messages)
system = "You are a helpful assistant. Respond in JSON."

# messages — user and assistant only
messages = [
    {"role": "user", "content": "What is RAG?"},
    {"role": "assistant", "content": "RAG stands for Retrieval-Augmented Generation..."},
    {"role": "user", "content": "How does it differ from fine-tuning?"},
]
Role Notes
system Top-level parameter, not a message
user Human input
assistant Previous model output (for multi-turn)

Extended thinking

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=16000,
    system="Think step-by-step before answering.",
    messages=[{"role": "user", "content": "Solve: If a train travels 120km in 2 hours, what's its average speed?"}],
    thinking={
        "type": "enabled",
        "budget_tokens": 10000,        # how much thinking is allowed
    },
)
# response.content has both thinking blocks and text blocks
for block in response.content:
    if block.type == "thinking":
        print("THINKING:", block.thinking)
    elif block.type == "text":
        print("ANSWER:", block.text)

Vision (image understanding)

import base64

with open("image.jpg", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{
        "role": "user",
        "content": [
            {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": b64}},
            {"type": "text", "text": "What is in this image?"},
        ],
    }],
)

Tool use

tools = [{
    "name": "get_stock_price",
    "description": "Get the current stock price for a ticker symbol.",
    "input_schema": {
        "type": "object",
        "properties": {
            "ticker": {"type": "string", "description": "Stock ticker, e.g. AAPL"},
        },
        "required": ["ticker"],
    },
}]

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system="You are a financial assistant. Use tools to look up data.",
    messages=[{"role": "user", "content": "What's the price of AAPL?"}],
    tools=tools,
)

# handle tool use
for block in response.content:
    if block.type == "tool_use":
        print(f"Tool: {block.name}, Input: {block.input}")

# send tool result back
import json
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [{
    "type": "tool_result",
    "tool_use_id": block.id,
    "content": json.dumps({"price": 195.50}),
}]})

Streaming

with client.messages.stream(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Write a haiku about coding."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Structured output

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system="Return a JSON object with keys: summary, sentiment, topics (array).",
    messages=[{"role": "user", "content": text}],
)
# parse response.content[0].text as JSON

Practical Examples

Example 1 — Long document analysis (200K context)

import anthropic

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

def analyze_long_document(document_text, question):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        system="You are an expert document analyst.",
        messages=[{
            "role": "user",
            "content": f"Document:\n\n{document_text}\n\nQuestion: {question}",
        }],
    )
    return response.content[0].text

Example 2 — Multi-turn coding assistant

import anthropic

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

messages = []
system = "You are a Python expert. Write clean, well-commented code."

def ask_coding(question):
    messages.append({"role": "user", "content": question})
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        system=system,
        messages=messages,
    )
    answer = response.content[0].text
    messages.append({"role": "assistant", "content": answer})
    return answer

Example 3 — Tool use with retry

import anthropic, json

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

def call_with_tools(messages, tools):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        messages=messages,
        tools=tools,
    )
    # check if tool use is needed
    tool_calls = [b for b in response.content if b.type == "tool_use"]
    if not tool_calls:
        return response.content[0].text

    # execute tools and continue
    messages.append({"role": "assistant", "content": response.content})
    for tc in tool_calls:
        result = execute_tool(tc.name, tc.input)    # your function
        messages.append({"role": "user", "content": [{
            "type": "tool_result", "tool_use_id": tc.id, "content": json.dumps(result),
        }]})
    return call_with_tools(messages, tools)          # recurse

Common Patterns

Pattern: system message as output schema

system = """You are a structured data extractor.
Always respond with valid JSON matching this schema:
{
  "entities": [{"name": str, "type": str}],
  "summary": str,
  "confidence": float (0-1)
}
Never include markdown code fences in your response."""

Pattern: batch processing with concurrent calls

import anthropic
from concurrent.futures import ThreadPoolExecutor

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

def process_item(text):
    response = client.messages.create(
        model="claude-haiku-4-20250414",
        max_tokens=256,
        messages=[{"role": "user", "content": f"Classify: {text}"}],
    )
    return response.content[0].text

with ThreadPoolExecutor(max_workers=5) as pool:
    results = list(pool.map(process_item, texts))

Pattern: extended thinking for complex reasoning

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=16000,
    system="You are a mathematical problem solver. Think step by step.",
    messages=[{"role": "user", "content": complex_problem}],
    thinking={"type": "enabled", "budget_tokens": 10000},
)

Best Practices

  • System is a top-level parameter, not a message — different from OpenAI.
  • Use claude-haiku-4-20250414 for classification/extraction — cheapest and fastest.
  • Use claude-sonnet-4-20250514 for most tasks — good balance.
  • Use claude-opus-4-20250514 for complex reasoning, research, analysis.
  • Claude excels at long context — 200K tokens for document analysis.
  • Extended thinking helps with math, logic, and multi-step reasoning.
  • Tool use is deeply integrated — always use tools for data access instead of relying on the model's knowledge.
  • Streaming via client.messages.stream() is more ergonomic than the raw stream API.

Common Mistakes

Mistake Fix
Putting system in messages array Claude requires system as a top-level parameter
Not streaming for user-facing apps Use client.messages.stream()
Ignoring extended thinking Enable for complex reasoning tasks
Not handling tool use recursion Tools may require multiple rounds — recurse until done
Using opus for simple tasks Use haiku for classification, extraction
Not checking response.stop_reason "end_turn" vs "max_tokens" tells you if output was truncated

Troubleshooting

Symptom Cause / Fix
AuthenticationError Invalid API key — check ANTHROPIC_API_KEY
RateLimitError Too many requests — implement backoff
overloaded_error Anthropic servers busy — retry after delay
Output truncated Hit max_tokens — increase it
Tool use not triggered Tool description may be unclear — improve it
JSON parse error Add "respond only with JSON" to system message
Slow responses Use extended thinking mode or switch to haiku

Official Documentation

Last Updated

2026-09-06 — Initial version.