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 |
$$$$ |
# 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?"},
],
}],
)
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
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.