Sunday, 06 September 2026
Advertisement Advertise Your advert could be here Reach thousands of learners and ICT professionals across Rwanda. Contact us
Advertisement Opportunity Jobs, scholarships & hackathons Fresh openings from Rwandan job boards are pulled in every hour. See openings

Your first program that calls a model

Building AI applications with Python · lesson 2 of 12

In this lesson: Write a working call, extract the text safely, and read the token usage.

Here is the whole thing, and then the parts that matter.

import anthropic

client = anthropic.Anthropic()   # reads ANTHROPIC_API_KEY from the environment

response = client.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    system="You are a careful assistant. If you are unsure, say so.",
    messages=[
        {"role": "user", "content": "Explain gradient descent in three sentences."}
    ],
)

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

print(response.usage.input_tokens, response.usage.output_tokens)

The client

anthropic.Anthropic() with no arguments resolves the key from the environment. Passing the key as a literal in code is the single most common way secrets end up in a public repository — do not do it, not even temporarily.

system versus messages

system is a separate top-level parameter, not a message with role="system". It holds standing instructions — persona, rules, output policy — and stays constant while messages changes. Keeping it separate is also what makes caching work later.

max_tokens is a ceiling on the reply

It limits output only, and the model does not know about it. Set it too low and answers are cut off mid-sentence with stop_reason == "max_tokens". Sensible defaults: about 16000 for ordinary non-streaming requests, around 256 for classification, and much larger with streaming when you genuinely need long output.

Reading the response safely

Never write response.content[0].text. The first block may be a thinking block or a tool call, and your code will crash the first time the model reasons. Filter by type — this habit costs nothing now and saves a production incident later.
text = "".join(b.text for b in response.content if b.type == "text")

Always check stop_reason

stop_reasonMeaningWhat your code should do
end_turnFinished normallyUse the text
max_tokensHit your ceilingRaise the limit or ask for less
tool_useWants to call a toolRun it and continue the loop
refusalDeclined on safety groundsHandle it — do not read the text and hope

Multi-turn conversation

Because the API is stateless, you keep the list and append to it:

messages = []

def ask(question: str) -> str:
    messages.append({"role": "user", "content": question})
    response = client.messages.create(
        model="claude-opus-5", max_tokens=16000, messages=messages,
    )
    messages.append({"role": "assistant", "content": response.content})
    return "".join(b.text for b in response.content if b.type == "text")

Note that the assistant turn is appended as response.content — the block list, not the extracted string. Appending only the text quietly discards tool calls and thinking, and breaks the next turn.

Errors are typed — catch specifically

try:
    response = client.messages.create(...)
except anthropic.RateLimitError:
    ...   # back off and retry
except anthropic.APIConnectionError:
    ...   # network — retry
except anthropic.BadRequestError:
    ...   # your request is wrong; retrying will not help
except anthropic.APIError as e:
    ...   # everything else

One broad except loses the distinction between "retry in two seconds" and "this will fail forever". The SDK already retries connection errors, 429 and 5xx a couple of times on its own.

Try it yourself

Write a script that asks the same question at max_tokens=50 and at max_tokens=2000, and prints stop_reason and both token counts each time. Seeing max_tokens appear in the first case makes the ceiling concrete in a way that reading about it does not.

Create a free account to save progress

All lessons in this track

  1. 1
  2. 2
  3. 3
  4. 4
    Getting JSON your program can rely on ~22 min account needed
  5. 5
    Giving the model tools it can call ~25 min account needed
  6. 6
  7. 7
  8. 8
  9. 9
    Making retrieval actually work ~20 min account needed
  10. 10
  11. 11
    Cost, latency and prompt caching ~22 min account needed
  12. 12
Advertisement Yanjye Learn a new digital skill this week ICT, programming and professional courses with graded weekly assignments. Start free