Claude API Tutorial — Your First Integration

This Claude API tutorial takes you from an empty folder to a working Python integration — install, authentication, your first call, streaming, and production-grade error handling — in about twenty minutes. Every code block below runs as-is against the current API (July 2026), using current model IDs and current request shapes. No stale temperature parameters, no deprecated thinking budgets — just the patterns I use in code that’s actually deployed.

TL;DR: pip install anthropic, export ANTHROPIC_API_KEY, then call client.messages.create() with a model ID, max_tokens, and a messages list. Read text from message.content, stream anything long with client.messages.stream(), and catch the SDK’s typed exceptions rather than string-matching errors. Start on claude-sonnet-5.

Prerequisites

You need Python 3.8+ and an Anthropic API key. If you don’t have a key yet, the Claude AI API key guide covers Console signup, billing, and key generation in five minutes — come back here once echo $ANTHROPIC_API_KEY prints something. TypeScript developer? The flow is identical with npm install @anthropic-ai/sdk; I’ll note the differences at the end.

Step 1: Install the SDK and set your key

python -m venv .venv && source .venv/bin/activate
pip install anthropic

export ANTHROPIC_API_KEY="sk-ant-your-key-here"

The SDK reads ANTHROPIC_API_KEY automatically — you should never pass the key in code. For projects, put it in a .env file (git-ignored) and load it with python-dotenv.

Step 2: Your first Claude API call

import anthropic

client = anthropic.Anthropic()

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Write a haiku about version control."}
    ],
)

print(message.content[0].text)
print(f"Tokens: {message.usage.input_tokens} in, {message.usage.output_tokens} out")

Three parameters are required: model (use claude-sonnet-5 as your default — the tiers are compared in the complete Claude API guide), max_tokens (a hard cap on output length), and messages (the conversation so far, alternating user and assistant roles). Run it and you’ll get a haiku plus exact token counts — get in the habit of logging that usage object now, because it’s how you’ll track spend later.

Step 3: Understand the response object

The response’s content field is a list of blocks, not a string. Today your requests return a single text block, but the moment you enable tool use or adaptive thinking, other block types appear first — so index-zero shortcuts eventually break. The robust pattern costs two lines:

text = "".join(block.text for block in message.content if block.type == "text")

if message.stop_reason == "max_tokens":
    print("Warning: response was truncated — raise max_tokens")

Also check stop_reason: end_turn means a natural finish, max_tokens means truncation, and tool_use means Claude is asking you to run a tool.

Step 4: Add a system prompt and conversation history

The system prompt sets persistent behavior and lives in its own system parameter, not in messages. Multi-turn conversation is just the message list growing — the API is stateless, so you resend history each turn:

history = []

def chat(user_input: str) -> str:
    history.append({"role": "user", "content": user_input})
    message = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        system="You are a concise senior Python reviewer. Answer in under 120 words.",
        messages=history,
    )
    reply = message.content[0].text
    history.append({"role": "assistant", "content": reply})
    return reply

print(chat("What does the walrus operator do?"))
print(chat("Show me a one-line example."))  # remembers the previous turn

Step 5: Stream responses like a real app

Waiting three seconds for a blank screen kills UX, and non-streaming requests with large max_tokens can hit HTTP timeouts. Streaming solves both — tokens render as they generate:

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

    final = stream.get_final_message()  # complete message object, with usage

The context manager handles the server-sent events plumbing; get_final_message() gives you the assembled response afterward for logging. My rule: anything above ~4K expected output tokens gets streamed, no exceptions.

Step 6: Handle errors properly

The SDK raises a typed exception per HTTP status and automatically retries rate limits and server errors twice with backoff. Catch specific classes, most specific first:

import anthropic

try:
    message = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
    )
except anthropic.AuthenticationError:
    raise SystemExit("Bad or missing API key — check ANTHROPIC_API_KEY")
except anthropic.RateLimitError:
    print("Rate limited after SDK retries — queue and retry later")
except anthropic.APIStatusError as e:
    print(f"API error {e.status_code}: {e.message}")
except anthropic.APIConnectionError:
    print("Network problem — check connectivity and retry")

A 400 at this stage usually means a request-shape mistake — the classic is copying temperature=0.7 from an old tutorial, which current models (Opus 4.8, Sonnet 5) reject outright. If you’re hitting 429s regularly, that’s a tier question, covered in the Claude API rate limits guide.

Step 7: Turn on adaptive thinking for hard problems

Everything so far returns fast, direct answers. For genuinely hard problems — debugging a race condition, multi-step math, planning a refactor — you can let Claude reason before it answers. On current models this is adaptive: you don’t set a token budget, Claude decides when and how much to think, and you tune overall depth with the effort parameter:

message = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=16000,
    thinking={"type": "adaptive"},
    output_config={"effort": "high"},  # low | medium | high | xhigh | max
    messages=[{"role": "user", "content": "Why does this test fail intermittently? ..."}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)

Two things to notice. The response may now contain thinking blocks before the text block — which is exactly why Step 3 taught you to filter by block type instead of grabbing index zero. And thinking tokens count against max_tokens and your bill, so reserve high effort for requests that earn it; low effort on Sonnet 5 is plenty for routine work.

What will this Claude API tutorial cost to run?

Almost nothing — and it’s worth understanding why. Every example on this page together sends well under 10,000 input tokens and generates a few thousand output tokens. On Sonnet 5’s introductory pricing ($2 per million input tokens, $10 per million output), the whole session costs a fraction of a cent. That’s the practical upside of logging message.usage from your very first call: you’ll know your real per-request cost before you ever ship, instead of estimating it from an invoice later.

Bonus: the same tutorial in TypeScript

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // reads ANTHROPIC_API_KEY

const message = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a haiku about version control." }],
});

const block = message.content[0];
if (block.type === "text") console.log(block.text);

Same request shape, same response structure, same error classes under Anthropic.*. The streaming helper is client.messages.stream() with a finalMessage() method. For a full JavaScript walkthrough — Express endpoints, streaming to the browser, and production patterns — see the Claude API Node.js guide.

Common mistakes in a first Claude API integration

  • Copying pre-2026 sample code. temperature, top_p, and fixed thinking budgets (budget_tokens) all return 400s on current models. If you want deeper reasoning, use thinking={"type": "adaptive"} instead.
  • Hardcoding the API key. It works until the commit. Environment variable from minute one.
  • Treating content as a string. It’s a list of typed blocks — filter for type == "text".
  • Forgetting the API is stateless. Claude doesn’t remember your last request; you must resend the conversation history every turn.
  • Setting max_tokens too low. Truncated JSON and half-finished code blocks are usually just a stingy cap. 1024 is a sane floor for chat; go much higher (with streaming) for generation tasks.
  • No usage logging. Wire message.usage into your logs today; your future self reading the invoice will thank you. Rates are broken down in the Claude API pricing guide.

Next steps

You now have the complete request-response loop: authenticated calls, system prompts, multi-turn history, streaming, and typed error handling. From here, go deeper with Claude API with Python for tool use, vision, and async patterns, or explore practical workflows in Claude for coding. The full endpoint reference lives in Anthropic’s official documentation, and everything else on this site’s API track is indexed at the API hub.

FAQ

What do I need before starting this Claude API tutorial?

Python 3.8 or newer, an Anthropic Console account at platform.claude.com, and an API key stored in the ANTHROPIC_API_KEY environment variable. The SDK installs with pip install anthropic, and the whole setup takes about five minutes.

Which model should a beginner use with the Claude API?

Start with claude-sonnet-5. It offers near-Opus quality at 60% of Opus output price (40% at introductory rates), with introductory pricing of $2 per million input tokens through August 31, 2026. Switch specific routes to claude-haiku-4-5 for cheap high-volume tasks or claude-opus-4-8 for the hardest reasoning.

Why does my old sample code return a 400 error?

Most likely it sends temperature, top_p, or a thinking budget_tokens value. Those parameters are removed on current models like Opus 4.8 and Sonnet 5 and the API rejects them. Delete the sampling parameters and use adaptive thinking instead of fixed budgets.

Does the Claude API remember previous messages?

No, the Messages API is stateless. Your application keeps the conversation history and resends it in the messages array on every call. Prompt caching makes resending long histories cheap, since cached prefixes are billed at a tenth of the normal input rate.

When should I use streaming instead of a normal request?

Stream whenever a user is watching the output or whenever you expect long responses. Streaming renders tokens as they generate and avoids HTTP timeouts that can affect non-streaming requests with large max_tokens values. The Python SDK handles it with client.messages.stream().

ClaudeAI.Guide Editorial Team

ClaudeAI.Guide Editorial Team

Independent editorial team behind ClaudeAI.Guide — an unofficial, third-party reference that is not affiliated with, endorsed by, or sponsored by Anthropic, PBC. We cover Anthropic’s Claude AI assistant from a practitioner’s perspective: hands-on tutorials, practical prompts, model comparisons (Claude Opus, Sonnet, Haiku), API walkthroughs, and honest reviews grounded in our own daily use. Everything we publish is tested in real workflows and verified at the time of writing, with no affiliate-driven hype. “Claude” and “Anthropic” are trademarks of Anthropic, PBC, used here for descriptive, nominative fair-use purposes only.

Articles: 72