Theory — Claude API Setup & First Calls
Understanding authentication, the Messages API, model selection, streaming, and error handling.
1. Introduction — Why the API Matters
Claude.ai gives you a polished chat interface to interact with Claude in your browser. It is great for exploration, brainstorming, and one-off tasks. But it is not programmable. You cannot embed it in your own application, automate it inside a data pipeline, or build a product on top of it.
The Anthropic API changes that. It exposes Claude's language capabilities as a network service you can call from any programming language, on any server, as many times as you need. With the API you can:
- Integrate Claude into web apps, mobile apps, and backend services
- Process thousands of documents automatically in a batch job
- Build autonomous agents that reason and act across multiple turns
- Combine Claude with databases, search engines, and external tools
- Fine-tune your prompts programmatically and measure the results
In short: Claude.ai is a consumer product; the API is the infrastructure layer that lets you build your own products and workflows on top of Claude's capabilities. This lesson walks through everything you need to go from zero to a working API call in Python.
2. Authentication — Keeping Your Key Safe
Every API call you make is authenticated with a secret API key. This key is tied to your Anthropic account and is used to track usage and bill you accordingly. Protecting it is your responsibility.
Obtaining a Key
Log in to console.anthropic.com, navigate to
API Keys in the sidebar, and click Create Key. Give it a meaningful name
(e.g., lesson02-dev) so you can identify it later. Copy the key immediately — it is shown
only once and cannot be retrieved afterwards. If you lose it, you must create a new one and revoke the old one.
The Golden Rule: Never Hardcode Keys
The most common beginner mistake is writing the key directly into source code:
# NEVER do this
client = anthropic.Anthropic(api_key="sk-ant-api03-abc123...") # ← visible in git history forever
If this file is ever pushed to a public (or even private) repository, your key is compromised. GitHub, GitLab, and similar platforms are routinely scraped for leaked credentials. The consequences range from unexpected API bills to account suspension.
The Correct Pattern: Environment Variables + python-dotenv
The standard solution is to store the key in an environment variable and load it at runtime. The
python-dotenv library makes this easy by reading a .env file in your project root:
# .env (NEVER commit this file)
ANTHROPIC_API_KEY=sk-ant-api03-your-real-key-here
# .env.example (DO commit this — it shows the shape without secrets)
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
# .gitignore (prevents .env from being committed accidentally)
.env
*.pyc
__pycache__/
from dotenv import load_dotenv
import anthropic
load_dotenv() # loads .env into os.environ before anything else
# Anthropic SDK reads ANTHROPIC_API_KEY from os.environ automatically
client = anthropic.Anthropic()
.env.
Check your usage dashboard for any unexpected charges.
3. The Messages API
All text generation with Claude goes through a single endpoint: POST /v1/messages. The Python SDK
wraps this as client.messages.create(). Understanding its structure is the foundation of everything
else in this course.
Request Parameters
| Parameter | Required | Type | Description |
|---|---|---|---|
model | Yes | str | The Claude model to use (e.g., claude-3-5-haiku-20241022) |
max_tokens | Yes | int | Hard cap on output tokens. The model stops here even mid-sentence. |
messages | Yes | list[dict] | Conversation turns. Each dict has role ("user" or "assistant") and content (str). |
system | No | str | System prompt — sets Claude's persona, tone, and constraints for the entire conversation. |
temperature | No | float | Sampling temperature 0–1. Lower = more deterministic. Default: 1. |
stop_sequences | No | list[str] | Stop generating when any of these strings appear in the output. |
A Complete Request and Response
import anthropic
from dotenv import load_dotenv
load_dotenv()
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=256,
system="You are a concise technical writer.",
messages=[
{"role": "user", "content": "What is an API, in one sentence?"}
]
)
# Accessing the response
print(response.content[0].text)
# → "An API (Application Programming Interface) is a set of rules and
# protocols that allows different software applications to communicate
# with each other."
print(response.usage.input_tokens) # → 28
print(response.usage.output_tokens) # → 32
print(response.stop_reason) # → "end_turn"
print(response.id) # → "msg_01XFDUDYJgAACzvnptvVoYEL"
The messages List and Multi-turn Conversations
Claude has no built-in memory between API calls. To simulate a conversation, you pass the entire history
in the messages list on every call. The list must start with a "user" message and
alternate strictly between "user" and "assistant" roles:
messages = [
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "The capital of France is Paris."},
{"role": "user", "content": "What is its population?"},
]
# Claude receives all three turns and responds to the last user message
4. Model Selection
Anthropic offers multiple Claude models optimised for different trade-offs between speed, cost, and capability. Choosing the right model for your use case is one of the most impactful decisions you can make — both for quality and for your API bill.
| Model ID | Speed | Cost (relative) | Best For |
|---|---|---|---|
claude-3-5-haiku-20241022 |
Very Fast | $ | Classification, sentiment, summaries, real-time chat, high-volume tasks |
claude-3-5-sonnet-20241022 |
Moderate | $$ | General tasks, coding assistance, document analysis, production applications |
claude-3-opus-20240229 |
Slow | $$$$ | Complex multi-step reasoning, nuanced writing, research, highest quality needs |
A Practical Decision Framework
Start with Haiku during development and for any use case where speed and cost matter more than perfection — classification, intent detection, simple Q&A, chat. Haiku is typically 10-20x cheaper than Opus and responds in a fraction of the time.
Promote to Sonnet when you need stronger reasoning, code generation, or analysis quality for a production application. Sonnet hits the sweet spot between capability and cost for the majority of tasks.
Reserve Opus for tasks where quality is paramount and cost is secondary — deep research synthesis, highly nuanced writing, complex multi-hop reasoning where Sonnet is not good enough.
5. Streaming
By default, client.messages.create() waits until Claude has generated the entire response,
then returns it all at once. For short responses this is fine. But for longer outputs — a multi-paragraph essay,
a code file, a detailed analysis — users can wait 10–30 seconds staring at a blank screen.
Streaming solves this. Instead of waiting for the full response, the API sends tokens as soon as they are generated, using a protocol called Server-Sent Events (SSE). From the user's perspective, text appears immediately and scrolls in as Claude "thinks". This makes the application feel dramatically faster even though the total generation time is the same.
Using the Stream Context Manager
import anthropic
from dotenv import load_dotenv
load_dotenv()
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-3-5-haiku-20241022",
max_tokens=512,
messages=[{"role": "user", "content": "Explain black holes to a 10-year-old."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
print() # add a newline after the stream ends
Key details:
- The
withblock (context manager) opens the HTTP connection and ensures it is closed cleanly when done. stream.text_streamis an iterator that yields string chunks — just the text, no metadata.end=""preventsprint()from adding a newline after every chunk.flush=Trueforces Python to write each chunk to the terminal immediately rather than buffering.
6. Error Handling
Production API code must handle failures gracefully. The Anthropic SDK raises typed exceptions so you can catch specific error conditions and respond appropriately.
| Exception | Cause | Recommended Action |
|---|---|---|
anthropic.RateLimitError | Too many requests; quota exceeded | Retry with exponential backoff |
anthropic.APIStatusError | HTTP 4xx/5xx from the server | Log the status code; retry 5xx only |
anthropic.APIConnectionError | Network connectivity issue | Retry with backoff; check internet |
anthropic.AuthenticationError | Invalid or missing API key | Check your .env; do not retry |
Exponential Backoff Pattern
import time
import anthropic
def call_with_retry(client, prompt: str, max_retries: int = 3) -> str:
"""Call the API with exponential backoff on rate limit errors."""
for attempt in range(max_retries):
try:
response = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=256,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
except anthropic.RateLimitError:
wait = 2 ** attempt # 1s, 2s, 4s
print(f"Rate limited. Retrying in {wait}s... (attempt {attempt + 1}/{max_retries})")
time.sleep(wait)
except anthropic.APIConnectionError as e:
print(f"Connection error: {e}. Retrying...")
time.sleep(2 ** attempt)
raise RuntimeError(f"API call failed after {max_retries} retries")
tenacity library, which provides a
battle-tested retry decorator with jitter, configurable wait strategies, and logging out of the box.
7. Further Reading
- Anthropic API Reference — Messages — complete parameter documentation
- Claude Model Overview — up-to-date list of available models with context windows and pricing
- anthropic-sdk-python on GitHub — source code, changelog, and advanced examples
- Streaming Guide — detailed documentation on SSE streaming and event types
- API Errors Reference — complete list of error codes and recommended handling