← Blog

Claude API Python SDK: Quickstart Tutorial

2026-09-24 · 5 min read · SubToAPI Team

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:

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:

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.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →