hands-on labIDE in your browser

Call Web APIs Safely: Keys, Errors, Retries and Rate Limits

Call a real-behaving web API from Python with requests the way production code does: keep the key in the environment and out of git, send parameters and headers with a timeout, treat 404, 401 and silence differently, retry only what can succeed with exponential backoff and Retry-After, and page through results under a rate limit before handing them to a language model and checking its answer against the data.

time
60 min
checked steps
5
level
Beginner
setup
None

$29.99a month with Pro

Pro opens all 5 steps of this lab and every other lab. Cancel any time.

no account needed · Pro opens all 5 steps

See Pro plans
Lab cockpit60 min · 5 stepsSession running
2 / 5 steps passingWhen the answer is no: status codes and timeouts · step 3 of 5
books.py▶ Run✓ Check
# ---------- Step 3: when the answer is no ----------def get_book(book_id, timeout=5):    """The book as a dict, or None if the API says it does not exist (404)."""    s = load_settings()    
TerminalOutput
step 3 of 5, replayed: When the answer is no: status codes and timeouts. The code, the output and the check are the lab's own.

The job

Brightline Books has access to its supplier's catalogue API. The lab runs a copy of that API next to your code, and it behaves like the real thing: it wants a key, it answers 404 for unknown books, one endpoint is slow, one fails until it recovers, and it allows four requests a second. You write books.py to read the key from the environment, search the catalogue, tell the kinds of failure apart, retry the ones that can succeed and fetch every page, then ask a model to recommend a book and check that the book really is in stock.

5 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    Keep the key out of the code

    Brightline's book supplier has given you access to its catalogue API.

    you writeload_settings()read this step below ↓

  2. 2

    Your first request: GET, parameters, headers

    A web API is a set of addresses that answer with data instead of web pages.

    you writesearch()

  3. 3

    When the answer is no: status codes and timeouts

    Status codes come in families, and each family calls for a different reaction:

    you writeget_book()

  4. 4

    Retry what can succeed later, and only that

    Some failures fix themselves: a 503 while the server restarts, a 429 because you were too fast, a dropped connection.

    you writeget_with_retry()

  5. 5

    Every page under a rate limit, then one grounded AI answer

    APIs rarely return everything at once.

    you writefetch_all()recommend()

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: Keep the key out of the code

Brightline's book supplier has given you access to its catalogue API. The lab runs a copy of it (bookapi.py; Run starts it for you), and like almost every paid API it wants a key with each request. Whoever holds the key can spend your money, so it must never sit in your code: code gets shared, pasted into chats and pushed to GitHub, where bots scan new commits for keys within minutes.

The standard arrangement:

  • The key lives in an environment variable, a named value the operating system hands to your program. Python reads it with os.environ.get("NAME").
  • On your own machine, you keep those variables in a file called .env (open it: it is already here) and the python-dotenv library loads it. load_dotenv() copies each NAME=value line into the environment, without overwriting anything already set, so the real environment on a server wins.
  • .env is listed in .gitignore, so git never commits it.

do this

1. Add .env to .gitignore. One line, under the TODO comment.

2. Write load_settings() in books.py: call load_dotenv(), read BOOKS_API_URL and BOOKS_API_KEY from os.environ, and if either is missing raise a RuntimeError that names both, so whoever sets up the program knows what to add. Return {"url": url.rstrip("/"), "key": key}. (rstrip("/") drops a trailing slash so url + "/books" never becomes //books.)

3. Run. It prints the settings with the key masked, then shows what the API says to a request without a key.

.gitignore, the file you edit3 lines
__pycache__/
*.pyc
# TODO (Step 1): add one line below this comment so git never picks up the .env file

provided for you:.envbookapi.pybooks.pytry_it.py

what you get

  • An editor and terminal in your browser
  • A check at the end of every step
  • Hints and the full solution
  • Nothing to install or configure

before you start

  • Basic Python: functions, dictionaries, loops, try/except

Part of the Start with AI track

Lab 12 of 21. Your first AI call, a chatbot, tested prompts and everyday automations. No machine learning background needed.

Questions about this lab

Why not just put the API key in the script?

Scripts get shared, pasted and committed, and public repositories are scanned for keys within minutes. Reading the key from an environment variable, loaded from a .env file that git ignores, keeps it out of every copy of the code.

Which errors should be retried?

Temporary ones: 429 Too Many Requests, 5xx server errors, timeouts and dropped connections. A 400, 401, 403 or 404 fails the same way on every try, so retrying only adds load. The lab's checker counts the calls your code makes to prove it.

What is exponential backoff?

Waiting longer after each failure, for example 0.5, 1 and 2 seconds, so a struggling server gets time to recover. When the server sends a Retry-After header, that value is used instead.

Does the lab need internet access?

No. The API runs inside the lab as a small Python server, which is what lets it fail on cue. The final step also calls a hosted language model through the lab's proxy.

What calling an API safely means

Every AI application is a client of web APIs: the model provider, a vector database, a search service, your own backend. The happy path is one line of requests.get. Production code needs the rest: a key that never appears in the source, a timeout on every call, a clear reaction to each class of status code, retries that back off and stop, and pagination that survives a rate limit. In this lab a local book-catalogue API reproduces each of those conditions on demand, so every behaviour is tested, not described. You load secrets with python-dotenv and keep .env out of git, send query parameters and an API key header, return None for a 404 while letting a 401 raise, time out a server that goes silent, write a retry loop with exponential backoff that honours Retry-After and never retries a 400, follow next links through every page, and finish with a language model whose answer is checked against the data it was given.