AI Engineering — Building Apps with Large Language Models · Lesson 2 of 12 · Updated ·
Your First LLM API Call — Keys, the Python SDK, Responses, Tokens and Cost
Chat websites are for people. APIs are for programs: your app sends a request and gets the model's answer back as data. We use Claude (by Anthropic) and its official Python SDK; other providers' APIs follow the same ideas.
1. Setup and keys
pip install anthropic
Create an API key in the provider's console and keep it secret:
- Put it in an environment variable —
ANTHROPIC_API_KEY— never in your code. - Never commit it to GitHub (bots scan public repos for keys within minutes).
- Never put it in a website or mobile app: anyone can extract it. Calls go through your server.
- Set a monthly spending limit in the console.
2. A first call
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1000, # upper limit on the answer's length
messages=[{"role": "user", "content": "Explain recursion to a first-year student in one sentence."}],
)
for block in response.content: # the answer is a list of content blocks
if block.type == "text":
print(block.text)
print("stop reason:", response.stop_reason)
print("tokens in/out:", response.usage.input_tokens, response.usage.output_tokens)
Output:
Recursion is when a function solves a problem by calling itself on a smaller version of the same problem, until it reaches a simple case it can answer directly (the base case).
stop reason: end_turn
tokens in/out: 38 51
(Output from our local stand-in API — a real model's wording will differ; the structure is identical.)
messagesis the conversation: alternatinguserandassistantturns.contentis a list of blocks — usually text; later you'll see tool calls too. Always checkblock.type.stop_reason:end_turn(finished),max_tokens(cut off — raise the limit),tool_use(wants a tool),refusal(declined for safety).usagereports the tokens you pay for.
Older tutorials set
temperature. On current Claude models sampling settings liketemperatureare no longer accepted — you steer the output with the prompt (and, for reasoning depth, aneffortsetting).
3. System prompts
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=500,
system="You are a patient programming tutor for Indian college students. Never give full solutions; ask a guiding question.",
messages=[{"role": "user", "content": "My loop prints numbers forever. Help?"}],
)
print(response.content[0].text)
Good question! Think about what changes each time the loop runs — what should the loop stop on?
The system prompt sets the role, rules and tone for the whole conversation; the user message is the specific request.
4. What does it cost?
Prices are per million tokens (MTok), different for input and output. Always check the provider's current pricing page; at the time of writing, Claude's models were priced like this:
PRICES_USD_PER_MTOK = { # (input, output) — check the current pricing page before you budget
"claude-opus-5-5": (4.00, 20.00),
"claude-sonnet-5-5": (2.00, 10.00),
"claude-haiku-5-5": (0.10, 0.50),
}
USD_TO_INR = 85 # use today's rate
def cost_inr(model, input_tokens, output_tokens):
price_in, price_out = PRICES_USD_PER_MTOK[model]
usd = input_tokens / 1e6 * price_in + output_tokens / 1e6 * price_out
return usd * USD_TO_INR
# a help-desk answer: 2,000 tokens of instructions + documents in, 300 tokens out
for model in PRICES_USD_PER_MTOK:
one = cost_inr(model, 2000, 300)
print(f"{model:18s} ₹{one:.3f} per answer ₹{one * 10_000:,.0f} per 10,000 answers")
claude-opus-5-5 ₹1.190 per answer ₹11,900 per 10,000 answers
claude-sonnet-5-5 ₹0.595 per answer ₹5,950 per 10,000 answers
claude-haiku-5-5 ₹0.030 per answer ₹298 per 10,000 answers
Rules of thumb: input tokens usually dominate (long instructions and documents), so keep prompts lean; use the most capable model where quality matters and test whether a smaller one is good enough for simple, high-volume tasks. Lesson 10 covers caching and other ways to cut costs.
5. Errors and retries
import anthropic
client = anthropic.Anthropic(max_retries=3, timeout=60) # the SDK retries 429/5xx automatically
try:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=300,
messages=[{"role": "user", "content": "Summarise: the team shipped login, found two payment bugs, fixing by Friday."}],
)
print(response.content[0].text)
except anthropic.RateLimitError:
print("Too many requests — slow down or queue them.")
except anthropic.APIStatusError as e:
print("API error", e.status_code, e.message)
except anthropic.APIConnectionError:
print("Network problem — check the connection.")
Summary: the team shipped the login feature, found two bugs in payments, and will fix them before Friday.
Catch specific errors: rate limits (429) need backing off, bad requests (400) need a code fix, server errors (5xx) can be retried.
6. Practise for free
Our lessons' code was checked against a small local stand-in for the API that answers in the same format (fixed, canned answers — not real intelligence). You can use it too when you don't have a key or credit yet: download llm_stub.py, run python llm_stub.py, then set ANTHROPIC_API_KEY=test and ANTHROPIC_BASE_URL=http://127.0.0.1:4010. Your code runs unchanged — later, remove those two settings and use a real key.
Common mistakes
- API key in the source code or in the browser.
- Reading
response.content[0].textwithout checking the block type orstop_reason. max_tokenstoo small → answers cut off (stop_reason == "max_tokens").- No spending limit while experimenting with loops.
Quick recap
- Key in an environment variable; calls go through your server.
client.messages.create(model, max_tokens, system, messages).- Read
contentblocks, checkstop_reason, watchusage. - Cost = input tokens × input price + output tokens × output price; catch specific errors.