Your First API Call 🔑
Everything so far has been you and Python. Now Python talks to a model running in a data centre. The code is short. The key discipline is the whole lesson.
Which model provider?
Every major provider works the same way: you send text over HTTPS, you get text back, you pay per token. This track uses Anthropic's Claude, because its Python library is clean, its documentation is excellent, and (relevant to this campus) its fastest tools are written in Rust. The shape of everything here transfers directly to OpenAI, Google, Mistral or a local model; only the import line and the model names change.
Learn the ideas here, not the exact function names. 'Send a list of messages, get a reply, loop for a conversation, stream for responsiveness, give it tools to act' is true of every chat model. Swapping providers later is an afternoon, not a rewrite.
Step 1: get a key, and understand what it is
Sign up at console.anthropic.com,
add a little credit (a few dollars lasts a long time at these token prices), and create an
API key. It looks like sk-ant-... and it is, in effect, a password that can
spend your money. Treat it exactly like one.
Bots scan every public commit on GitHub within seconds of it being pushed. A leaked cloud or model key has generated genuine four- and five-figure bills overnight. If you ever expose a key, revoke it immediately in the console, not later. And remember Lesson 52: deleting it in a new commit does not remove it from git history.
Step 2: keep the key out of your code
The key never, ever goes in a .py file. It goes in an environment variable,
loaded from a .env file that git is told to ignore. This is exactly the
pattern from Lessons 42 and 52, and here is why it exists.
# .env (this file is SECRET and gitignored, never committed)
ANTHROPIC_API_KEY=sk-ant-your-real-key-here
# .gitignore (add this on your very first commit, before you forget)
.env
.venv/
__pycache__/
Then load it. Two ways, both fine:
import os
# The bare way: the variable must already be in your shell's environment
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
raise SystemExit("Set ANTHROPIC_API_KEY. See the README. Never hard-code it.")
print("Key loaded:", api_key[:7] + "..." if api_key else "MISSING")
# The convenient way: python-dotenv reads the .env file for you
from dotenv import load_dotenv # pip install python-dotenv
import os
load_dotenv() # reads .env into the environment
api_key = os.environ["ANTHROPIC_API_KEY"]
Build the habit so deeply that hard-coding a key feels physically wrong. .env in .gitignore, on the first commit, every project, no exceptions.
The people who leak keys are not careless amateurs. They are experienced engineers in a hurry who typed the key in 'just for a second to test'. The second becomes a commit becomes a bill. The discipline is the skill.
Step 3: install the library
python3 -m venv .venv
source .venv/bin/activate # .venv\Scripts\activate on Windows
python -m pip install anthropic python-dotenv
A virtual environment (Lesson 26), then the official SDK plus the dotenv helper. On your own machine this is two minutes. In the browser it cannot run at all, which is why this whole level has no ▶ buttons: real assistants need the network and a key, and the school keeps both out of your browser on purpose.
Step 4: the first call
import anthropic
# The client reads ANTHROPIC_API_KEY from the environment automatically.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "In one sentence, what is Python good for?"},
],
)
# The reply is a list of content blocks. Pull the text out.
for block in response.content:
if block.type == "text":
print(block.text)
Take that apart, because every field matters:
| Piece | What it is |
|---|---|
anthropic.Anthropic() | The client. Finds your key in the environment on its own |
model="claude-opus-5" | Which model. Opus is the most capable; see the table below |
max_tokens=1024 | A hard ceiling on the reply length. Required. You pay for what comes back |
messages=[...] | The conversation, as a list of role/content dicts |
"role": "user" | Who is speaking: user is you, assistant is the model |
response.content | A list of blocks; the text lives in blocks of type text |
Choosing a model
| Model id | Best for | Rough cost per 1M tokens (in / out) |
|---|---|---|
claude-opus-5 | The hardest reasoning, coding, long tasks | $5 / $25 |
claude-sonnet-5 | The everyday workhorse: fast, cheap, very capable | $3 / $15 |
claude-haiku-4-5 | Simple, high-volume, latency-sensitive tasks | $1 / $5 |
For most of your Jarvis, claude-sonnet-5 is the right default: nearly the
capability of Opus at a fraction of the price and latency. Reach for Opus on genuinely
hard problems, Haiku for cheap bulk classification. A million tokens is a lot of text, so
a few dollars of credit goes further than you would think.
Model names and prices move. The exact strings above are current as this lesson was written; check the model docs and the pricing page before you rely on a number. The code shape does not change.
A system prompt: telling it who to be
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="You are Jarvis, a concise and slightly witty assistant. "
"Answer in at most two sentences. Never invent facts.",
messages=[{"role": "user", "content": "Should I bring an umbrella?"}],
)
print(response.content[0].text)
The system parameter sets the assistant's standing instructions: its
persona, its rules, its constraints. It is separate from the conversation and applies to
every turn. This is where your Jarvis gets its personality and its guardrails, and you
will spend real time tuning it.
When it goes wrong
import anthropic
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(response.content[0].text)
except anthropic.AuthenticationError:
print("Your API key is missing or wrong. Check ANTHROPIC_API_KEY.")
except anthropic.RateLimitError:
print("Too many requests. Wait a moment and retry.")
except anthropic.APIStatusError as err:
print(f"The API returned an error: {err.status_code}")
except anthropic.APIConnectionError:
print("Could not reach the API. Check your internet connection.")
The library raises typed exceptions, exactly the sort you handled in Lesson 22. Catch the specific ones you can do something about: a bad key, a rate limit, a network drop. The SDK already retries transient failures a couple of times on its own.
Make the call yours
On your own machine, with your key in .env, write a program that asks the model to explain one thing you have always wondered about, with a system prompt that makes it answer like a patient teacher. Run it. You are now talking to a model from your own code.
Reveal solution
from dotenv import load_dotenv
import anthropic
load_dotenv()
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=500,
system="You are a patient teacher. Explain clearly, use one analogy, "
"and check for the most common misconception at the end.",
messages=[{"role": "user", "content": "Why is the sky blue?"}],
)
print(response.content[0].text)The moment this prints a real answer is the moment this stops being a course and starts being a thing you built.
Audit for leaks
Here is a beginner's first script. Find the three security problems before it ever reaches GitHub.
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-api03-RealKey123")
response = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
messages=[{"role": "user", "content": input("Ask: ")}])
print(response.content[0].text)Reveal solution
- The key is hard-coded. The moment this is committed, the key is public and git history keeps it forever. Load it from the environment.
- No
.gitignoreshown, so even the fix (a.envfile) would get committed unless.envis ignored first. - No error handling and no
max_tokensdiscipline around user input: a hostile or accidental prompt can run up cost or crash with an unhandled exception.
The fix is everything in this lesson: environment variable, gitignore, typed exception handling. None of it is hard; all of it is habit.