10. Chapter review

You've just crossed a major threshold. You wrote code that connects to real servers across the internet, receives live data, and parses that data into structures you can work with. You're no longer just running scripts that live entirely on your computer. You're building programs that talk to the outside world.

In this chapter, you went from making your very first API call to building a small, multi-API program that feels like a real application. Along the way, you met the core ideas that show up in almost every API project: send a request, inspect the response, parse the data, and handle anything that goes wrong. The Cat Facts and Jokes program might be playful, but the way you designed it, with timeouts, error handling, and clean JSON parsing, is exactly how serious systems are built.

Key skills mastered

  • Making HTTP requests with Python. You can now use the requests library to send GET requests to any API endpoint, retrieve live data from servers, and bring that information into your Python programs. requests.get(url) is the core pattern for fetching data from the web.
  • Working with Response objects. You can check the status code with response.status_code, read the raw text with response.text, and parse JSON data into Python dictionaries and lists with response.json().
  • Understanding and parsing JSON. You can read JSON responses from APIs and navigate their structure just like you'd navigate Python dictionaries and lists. JSON is the universal format for structured data on the web, and you know how to extract the specific pieces you need from nested structures.
  • Handling errors and timeouts. You can wrap requests in try/except blocks, include timeouts so your program never hangs, and use raise_for_status() to turn silent HTTP errors into explicit exceptions you can handle.
  • Customising requests with parameters. You pass a dictionary to the params argument so that requests builds safe, properly formatted URLs for you, rather than concatenating query strings by hand.
  • Building multi-API programs. You built a complete program that integrates data from multiple APIs, formats the output in a user-friendly way, and handles errors for each request independently. This is the pattern you'll use in every real-world project.

Chapter review quiz

Test your understanding of the key concepts from this chapter:

Select a question to reveal the answer:
What's the difference between response.text and response.json()?

response.text gives you the raw response body as a string. Python sees characters; it doesn't know anything about structure.

response.json() parses that text as JSON and returns a Python object (usually a dictionary or list). Now you can access fields with keys, loop over lists, and treat the data like normal Python structures.

Why should every request include a timeout parameter?

Without a timeout, your program is willing to wait forever if a server never responds. That can freeze a script or lock up an entire application, forcing users to manually kill the process.

With a timeout (for example, timeout=10), the request fails fast with a clear exception after 10 seconds, giving you a chance to log the error, show a friendly message to the user, or try again later. A timeout is a simple way to make your code far more robust.

What does response.raise_for_status() do, and why is it useful?

response.raise_for_status() checks the HTTP status code and raises an HTTPError if it's in the 4xx or 5xx range. It turns silent failures into explicit exceptions you can catch in a try/except block.

The pattern it encourages is a healthy one: assume the request might fail, let raise_for_status() surface the problem, and handle it cleanly in your code.

Why is using the params argument better than building query strings by hand?

The params argument takes a dictionary and handles the low-level details for you:

  • It automatically URL-encodes spaces, special characters, and other symbols that aren't allowed in URLs.
  • It converts Python types (integers, booleans) to strings in the correct format.
  • It builds the ?key=value&key2=value2 part of the URL safely and correctly.

Manual string formatting leads to subtle bugs (forgetting to encode spaces or special characters). Let the library handle the details so you can focus on what the parameters mean, not how they're formatted.

What information does the response object give you beyond just the data?

The response object contains much more than just the body. It includes:

  • response.status_code, the HTTP status code (200, 404, 500) that indicates whether the request succeeded.
  • response.headers, a dictionary of HTTP headers the server sent back, which can include content type, caching info, and rate-limit details.
  • response.url, the final URL that was requested (useful if the request was redirected).
  • response.text and response.json(), the actual data returned by the API.

All of this metadata is useful for debugging, logging, and understanding exactly what happened during the request-response cycle.

What's the difference between a 4xx and a 5xx HTTP status code?

4xx status codes indicate client errors: the problem is with your request. 404 means the resource wasn't found, 400 means the request was malformed, 401 means you're not authenticated. These errors suggest you need to fix something about the request you're sending.

5xx status codes indicate server errors: the problem is on the server side. 500 means an internal server error, 503 means the service is temporarily unavailable. There may not be anything you can do except retry later or contact the API provider.

The distinction matters because 4xx errors often need code changes, while 5xx errors often just need retry logic or user notifications.

Looking forward

You've learned how to talk to an API. Next, you'll learn how to make those conversations resilient. Chapter 4 takes the basic patterns from this chapter and tightens them into the Make → Check → Extract defensive shape that every real API call you'll ever write will follow: timeouts that catch silent servers, status checks that catch error pages disguised as JSON, content-type validation that catches the response that quietly changed shape, and structure validation that catches keys that should be there but aren't.

The "ask, wait, inspect, use" rhythm from this chapter's overview is the same shape as Chapter 4's pattern, just one step tighter. By the end of Chapter 4 you'll have a reusable fetch_json_safely function and a small image downloader that survives every flavour of network failure cleanly, instead of crashing on the first thing it doesn't expect.