I still keep the first working Claude script I ever wrote. It lives in a folder labeled “failed experiments / early wins,” and it is embarrassingly short. Twelve lines of Python, a single environment variable, and a print statement. The first time it returned a coherent sentence I sat back in my chair in Boise and thought, “Okay. Now we can start building something real.”
Most tutorials stop at that moment of mild satisfaction. They show you the happy path and then move on to the next shiny feature. This post is different. We are going to build the smallest useful version of an Anthropic API call, watch it succeed, then deliberately try to break it in the ways that actually happen when you leave the playground. By the end you will have a working request, a clear picture of what the response object contains, and a short list of failure modes you should expect the first time you put this code in front of real work.
The method is the same one this site will keep returning to:
Build the smallest useful version, then test what breaks.
What You Will Have Working by the End
A clean Python environment with the official Anthropic SDK
An API key loaded safely from the environment (never hardcoded)
A single messages.create call that returns text
A short inspection of the response object so you understand stop reasons, usage, and content blocks
Three concrete ways the same code can fail and how to notice them early
No frameworks. No agent loops. No structured output yet. Just the foundation every later tutorial on this site will assume you already have.
Prerequisites
You need three things:
Python 3.10 or newer (the current Anthropic SDK requires it).
An Anthropic Console account and an API key. Create one at console.anthropic.com under Settings → API Keys. The key starts with sk-ant-.
A few dollars of credit on the account. The examples in this post will cost well under a dollar.
I develop on macOS and Linux. The same steps work on Windows if you use a proper virtual environment and set the environment variable correctly.
Step 1: Create an Isolated Environment
Never install the Anthropic package into your system Python. Create a project directory and a virtual environment:
Bash
mkdir anthropic-first-request
cd anthropic-first-request
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activateInstall the official SDK:
Bash
pip install anthropicThat is the only dependency for this post. Later articles will add python-dotenv when we start managing multiple keys and environments, but for the absolute minimum we can stay with the environment variable approach.
Step 2: Put the API Key Somewhere Safe
Open your terminal and export the key for the current session:
Bash
export ANTHROPIC_API_KEY="sk-ant-api03-..."The Anthropic client looks for this exact variable name by default. Do not put the key in your source code. Do not commit it to git. If you are using a .env file later, add .env to your .gitignore before you write a single line of application code.
I once watched a junior developer paste a key into a Slack thread “just for a minute.” The key was rotated within an hour, but the habit of treating secrets as temporary convenience is hard to unlearn. Treat the key like a password from the first minute.
Step 3: The Smallest Useful Request

Create a file called first_request.py:
Python
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-sonnet-5", # check docs.anthropic.com for the current recommended ID
max_tokens=256,
messages=[
{
"role": "user",
"content": "In one clear sentence, what is a context window in a language model?"
}
]
)
print(response.content[0].text)
print("---")
print(f"Stop reason: {response.stop_reason}")
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")Run it:
Bash
python first_request.pyYou should see a short, coherent sentence followed by the stop reason (end_turn in the normal case) and the token counts. That is the entire happy path.
A few notes on the choices I made:
I used claude-sonnet-5 because it is currently a strong balance of speed, cost, and quality for most development work. Model IDs change. Always verify the current recommended identifier in the official Models overview before you copy any example into production code.
max_tokens=256 is deliberately low. It forces the model to stay concise and keeps the cost of experimentation tiny. Raise it only when you have a reason.
The messages list is a list of dictionaries. Even for a single turn you still pass a list. This shape becomes important the moment you add conversation history or tool results.
What the Response Object Actually Contains
The object returned by messages.create is richer than the text you printed. Here is what I usually inspect first:
response.content — a list of content blocks. For a normal text reply the first (and usually only) block has type="text" and a .text attribute.
response.stop_reason — why the model stopped. Common values are end_turn, max_tokens, and later tool_use.
response.usage — input and output token counts. These numbers are what you will multiply by the current price to estimate cost.
response.model — the exact model that answered. Useful when you are comparing versions.
response.id — a unique identifier for the request. Keep it when you are debugging production traces.
I like to print these fields on every early experiment. It trains the habit of looking at the full picture instead of only the generated text.
Adding a System Instruction (Still Minimal)
Most real applications need a system prompt. The Anthropic Messages API accepts it as a top-level parameter, not as a message with role "system". Here is the same script with a short system instruction:
Python
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=256,
system="You are a precise technical writer. Answer in one or two short sentences. Do not use marketing language.",
messages=[
{
"role": "user",
"content": "In one clear sentence, what is a context window in a language model?"
}
]
)
print(response.content[0].text)The system parameter is the right place for durable instructions about tone, format, and constraints. Keep it short in early experiments. Long system prompts are a later optimization, not a first requirement.
Three Ways This Code Breaks (and How to Notice)
1. Missing or Invalid API Key
If ANTHROPIC_API_KEY is not set, the client raises an authentication error before any network call. The message is usually clear. The fix is equally clear: export the variable or load it from a secrets manager. Never catch this error and retry with a hardcoded fallback.
2. Hitting max_tokens
Change max_tokens to 20 and ask for a longer explanation. The model will stop mid-sentence and stop_reason will become max_tokens. This is not a failure of the model; it is the contract you set. In production code you should treat max_tokens as a hard limit you chose, not an unexpected truncation.
3. Network or Rate-Limit Errors
Transient network issues and rate limits raise exceptions from the SDK. The first time you see them, log the full exception and the request parameters (without the key). Retry with exponential backoff only for the transient cases. Do not retry authentication or invalid-request errors; those need a code change.
I keep a private “failure notebook” for every new API surface. The first three entries for Anthropic were exactly these three cases. Writing them down once saves hours later when the same errors appear inside a larger agent loop.

A Quick Cost Reality Check
Token prices change. At the time of writing, Sonnet-class models are priced such that a few dozen experimental requests cost cents, not dollars. Still, print the usage numbers on every early script. The moment you start looping over documents or running evaluation sets, the same habit will tell you whether the design is economically viable before you have invested days of engineering.
What This Code Does Not Do
It does not:
Maintain conversation history across multiple turns
Call tools or execute functions
Guarantee structured JSON
Handle streaming
Retry automatically
Log requests in a privacy-safe way
Those are all deliberate omissions. Each one will get its own focused post. The goal today is a foundation that is small enough to understand completely and solid enough that later tutorials can build on it without re-explaining the basics.
Verification Step You Can Run Right Now
Run the first script and confirm you receive a coherent sentence.
Print response.stop_reason and the usage numbers.
Temporarily unset ANTHROPIC_API_KEY and confirm you get a clear authentication error.
Set max_tokens=15 and confirm the model stops early with the expected stop reason.
Restore a sensible max_tokens value and put the key back.
If all five steps behave as described, you have a working baseline.
Update Note
Model identifiers, pricing, and the exact shape of the response object can change with new Anthropic releases. Before you copy any of this code into a longer-lived project, check the current Models overview and the Python SDK documentation on docs.anthropic.com (or platform.claude.com). The structure of client.messages.create has been stable for a long time, but the model strings are not.
Next Steps
The next post will cover authentication patterns, environment variables across local and production environments, and the small amount of defensive code that keeps keys out of logs and source control. After that we move into message structure, context windows, and the first tool-using agent.
For now, keep the twelve-line script. When something more complex fails later, you will be glad you still have a known-good request you can run in under a second.
Build the smallest useful version, then test what breaks.
No feedback yet — submit the first.