Claude API Python SDK: Quickstart Tutorial
Getting started with the Claude API in Python takes about five minutes: install the anthropic package, set your API key as an environment variable, and call client.messages.create() with a model name and a list of messages. That's the entire quickstart in one sentence — the rest of this guide walks through each step with working code, plus the details that trip people up the first time (system prompts, streaming, and handling errors).
If you just want a copy-paste starting point, skip to the "Minimal working example" section below. If you want to understand what each piece does and why, read straight through.
Prerequisites
Before writing any code you need:
- Python 3.8+ installed
- An Anthropic API key from the Anthropic console, or an application key from a proxy service if you're building a product on top of Claude rather than calling the raw API directly
- pip to install the SDK
pip install anthropic
That single package gives you the Anthropic client, typed request/response models, streaming helpers, and retry logic for rate limits.
Setting your API key
The SDK reads the key from the ANTHROPIC_API_KEY environment variable by default, so you never have to hardcode it:
export ANTHROPIC_API_KEY="sk-ant-..."
On Windows PowerShell:
$env:ANTHROPIC_API_KEY="sk-ant-..."
If you'd rather pass the key explicitly (useful in notebooks or CI), you can do that in the client constructor instead — shown below.
Minimal working example
Here's the smallest script that sends a message to Claude and prints the reply:
import anthropic
client = anthropic.Anthropic(
api_key="sk-ant-..." # or omit this and rely on ANTHROPIC_API_KEY
)
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Explain what a quickstart tutorial should cover."}
]
)
print(response.content[0].text)
Run it with python quickstart.py. You should see a plain-text response printed to the terminal within a couple of seconds.
A few things worth noting about this call:
model— pick a Claude model identifier; newer models generally cost more but reason better on complex tasks.max_tokens— required. This caps the length of the reply, not the input.messages— a list of turns, each with arole(userorassistant) andcontent. For a first request you typically only send oneusermessage.
Adding a system prompt
Most real applications need a system prompt to set behavior, tone, or constraints. This is a separate top-level parameter, not part of the messages list:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="You are a concise technical writer. Answer in bullet points only.",
messages=[
{"role": "user", "content": "What are the steps to parse a CSV file in Python?"}
]
)
Keep the system prompt short and specific — it gets sent with every request in the conversation, so verbose instructions add latency and cost on every turn.
Multi-turn conversations
Claude's API is stateless: you resend the full conversation history on every call. To build a chat loop, append each assistant reply back into the messages list before the next request:
messages = [{"role": "user", "content": "What's a good name for a note-taking app?"}]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=500,
messages=messages
)
reply = response.content[0].text
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": "Make it sound more playful."})
response2 = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=500,
messages=messages
)
There's no session or thread ID to manage server-side — the history you send is the entire context Claude has.
Streaming responses
For chat UIs, streaming tokens as they're generated feels much faster than waiting for the full response. The SDK exposes this with a context manager:
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a haiku about databases."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
This prints tokens to the terminal as they arrive instead of blocking until the full reply is ready.
Handling errors and rate limits
The SDK raises typed exceptions you can catch individually:
from anthropic import APIStatusError, RateLimitError
try:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}]
)
except RateLimitError:
print("Hit a rate limit — back off and retry")
except APIStatusError as e:
print(f"API returned an error: {e.status_code}")
Wrap production calls in retry logic with exponential backoff, especially if you're sending traffic from multiple processes or a queue worker.
When you need more than raw API access
The raw SDK is great for scripts, prototypes, and single-developer projects. Once you're shipping a product to real users, you usually need things the Anthropic API doesn't hand you out of the box: per-application API keys instead of one shared secret, usage breakdowns by key or by team member, and a dashboard non-engineers can check without reading logs.
That's the gap SubToAPI fills — it sits in front of your Claude access and issues scoped sub_live_... keys per application or per customer, while still speaking the same Messages API shape your Python code already expects. Swapping the base URL and key in the snippets above is enough to get streaming, tool use, and usage metadata without rewriting your integration. Check the quickstart or the full messages reference for the exact request format, and see pricing if you're comparing plans for a team.
Tool use and function calling
Claude can call functions you define by returning a tool_use block instead of plain text. This is a separate feature from the basic quickstart above — worth learning once you've got simple requests working, since it lets Claude fetch live data or trigger actions in your app. If you're building this on top of a proxied setup, the tools guide covers the request/response contract in detail.
questions
Do I need an Anthropic account to use the Python SDK? Yes, for direct access you need an API key from Anthropic's console. If you're using a proxy layer like SubToAPI, you generate keys from that dashboard instead after signing up, without touching Anthropic's console directly.
Why does my script hang or time out on long responses? Non-streaming calls block until the full response is generated, which can take longer for high max_tokens values or complex prompts. Switch to client.messages.stream() if you need faster perceived response times.
Can I use the same SDK code with a different backend, like SubToAPI? Yes — point the client's base_url at https://api.subtoapi.app/v1 and use your sub_live_... key. The request and response shapes match the standard Messages API, so existing SDK code generally works unchanged.