An API is a front desk for a program
When you open a web page, your browser sends a request to a server and gets a page back. An API is the same conversation, but the reply is data meant for a program rather than a layout meant for eyes. You send a request. You get a response. The response has a status code and a body, and the body is usually JSON, which lesson eight showed you how to turn into a dict.
That is the entire idea. Everything else is detail.
A call you can run right now
Inside an activated virtual environment with requests installed:
import requests
r = requests.get("https://api.github.com/repos/python/cpython", timeout=10)
print(r.status_code)
data = r.json()
print(data["full_name"])
print(data["stargazers_count"])
print(data["language"])No account, no key. Four things to notice.
requests.get sends the request and waits. timeout=10 says give up after ten seconds; without it a program can hang indefinitely, and a hang is harder to debug than a failure. Always pass a timeout.
r.status_code is the server's one-word summary of how the conversation went:
200— fine401— your credentials are missing or wrong403— recognised, but not allowed404— no such thing at that address429— too many requests, slow down500and up — the server broke, not you
r.json() parses the response body into Python dicts and lists. It fails if the body is not JSON, which is what happens when an error page comes back as HTML. If you are unsure what arrived, print r.text first.
Add r.raise_for_status() after the call and any 4xx or 5xx becomes an exception instead of quietly flowing into code that expects data.
Sending data, and sending a key
AI APIs need two more things: you send a body with POST instead of asking with GET, and you identify yourself with a key.
import os
import requests
API_KEY = os.environ["ANTHROPIC_API_KEY"]
MODEL = "claude-sonnet-4-5" # model ids change; check the provider's docs
r = requests.post(
"https://api.anthropic.com/v1/messages",
headers={
"x-api-key": API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json={
"model": MODEL,
"max_tokens": 300,
"messages": [
{"role": "user", "content": "Explain gravity in one sentence."}
],
},
timeout=60,
)
print(r.status_code)
reply = r.json()
print(reply["content"][0]["text"])You have seen every piece of that before. headers is a dict. json= takes a dict and requests converts it for you. The messages value is a list of dicts, exactly the structure you built in lessons four and six. The reply is a dict, and reply["content"][0]["text"] is a lookup, a position, and another lookup.
This one costs money and needs an account, so it may not be the one you run today. Every provider's shape differs slightly; read their docs for the exact keys. The pattern does not differ.
Keys
A key is a password that spends your money. Three rules.
Never type it into your code. Put it in the environment instead:
export ANTHROPIC_API_KEY="sk-..." # macOS or Linux
$env:ANTHROPIC_API_KEY = "sk-..." # Windows PowerShellNever commit it. If a key has ever been in a file you pushed anywhere public, treat it as leaked and replace it at the provider. Deleting the line in a later commit does not help; the old commit is still in the history, and automated scanners find published keys within minutes.
Never paste it into a chat, a screenshot, or a support ticket.
What the status code does and does not tell you
A 200 means the request was well formed and the server answered. It says nothing about whether the answer is true, complete, or sensible.
An AI model can return a confident, well-written, wrong answer with a 200. It can also stop mid-sentence because it hit your max_tokens limit, and that is still a 200, with a field in the response body such as stop_reason telling you why it ended. The status code describes the delivery. The body is where the content, and any problem with the content, lives.
Try this now
Run the GitHub call. Change cpython to something that does not exist and print the status code. Then wrap it:
import requests
def repo_stars(full_name):
r = requests.get(f"https://api.github.com/repos/{full_name}", timeout=10)
if r.status_code != 200:
return None
return r.json()["stargazers_count"]
for name in ["python/cpython", "pallets/flask", "no/such-repo"]:
print(name, repo_stars(name))A function, a loop, a dict, a list, a conversion, and an API call. That is the whole course in nine lines.
Before you move on