8. Chapter review
You started the chapter with four lines of Python that talked to an API on a good day, and you're leaving it with a production-shaped image downloader that keeps working when things go wrong. Before Part II, let's consolidate what stuck.
What you've built
The patterns from this chapter are the ones that separate code that works in a demo from code that survives in production. Every API call you write from here on should fit the Make → Check → Extract shape, validate at the right layers, and fail in a way the caller can actually recover from. That isn't paranoia; it's professionalism.
The skills to carry forward
- The universal pattern. Make → Check → Extract is now muscle memory. Apply it to every API request regardless of complexity.
- Validation layers. Check at the network layer (
try/except), the HTTP layer (status code), the format layer (Content-Type), and the structure layer (key existence). - Status code discipline.
response.okfor quick checks orraise_for_status()inside atry/except. Pick by context. - Header reading.
Content-Typeto prevent format mismatches, rate-limit headers to avoid being blocked,Content-Lengthfor size limits and progress bars. - Binary handling.
response.contentfor files,"wb"mode when writing. Never mix text and binary access methods. - Graceful degradation. Your code now fails with a clear message instead of a stack trace.
Underneath all of those is one mindset shift: stop assuming the happy path and start planning for the unhappy one. Networks drop, servers crash, responses arrive in surprising shapes, and your code should expect all of that. Validate everything before you use it, fail with readable messages instead of tracebacks, keep your return shape consistent across success and failure, and you'll have prevented the majority of production bugs before they happen.
Check your understanding
Click each question to reveal the answer. Try to articulate a response before you peek.
Describe the Make → Check → Extract pattern in your own words.
Make the request with a timeout. Check that the response is what you expected: status code first, then Content-Type if you're about to parse. Extract the data you need and validate its structure before using it. The three steps live inside a try/except so network failures route through the same handler as everything else.
What are the four validation layers in defensive API code?
Network layer (try/except for connection errors and timeouts), HTTP layer (status code), Format layer (Content-Type header), Structure layer (keys or fields exist in the parsed data). Skip any one and you're unprotected against that class of failure.
When should you use response.ok vs raise_for_status()?
Use response.ok for a quick boolean when you want to branch on success inline. Use raise_for_status() inside a try/except when you want HTTP errors routed through the same exception handler as network errors. Same underlying check, picked by whether the surrounding function is already exception-shaped.
What's the difference between response.text, response.json(), and response.content? When do you use each?
response.text returns the body as a decoded string, use it for plain text or HTML. response.json() parses the body as JSON into a dict or list, use it only when Content-Type confirms application/json. response.content returns raw bytes, use it for binary data like images, PDFs, or anything you'll write with "wb". Picking the wrong one crashes the program or corrupts the file.
What's the rule for reacting to a 429 response?
429 means "too many requests." Read the Retry-After header if present and wait exactly that many seconds before retrying. If there's no Retry-After, back off for a reasonable default (often 30-60 seconds). APIs that send 429s will block clients that keep hammering, so treat the response as a hard stop, not a hint.
Why do you need "wb" instead of "w" when saving binary data?
"w" opens the file in text mode, which applies character encoding and newline translation to what it writes. Binary data (images, PDFs, video) isn't text, any transformation corrupts it. "wb" opens in binary mode with no translation, so the bytes land on disk exactly as the server sent them.
Looking ahead
That rounds out the core request-and-response skills. Four chapters ago you were working out what an API even is, and now you can make requests, handle responses, validate data, and work with different content types. The image downloader isn't just an exercise; it's a template you'll reach for any time you need to pull files, sync media, or handle binary data. The validation layers apply to every API integration you'll ever build, and the Make → Check → Extract pattern works for REST, GraphQL, webhooks, and anything else that speaks request-and-response.
The rest of Part 1 finishes the foundations, and Part 2 (Building Core Skills) puts them to work on bigger projects:
- Chapter 5. HTTP methods (POST, PUT, DELETE) for sending data to APIs.
- Chapter 6. Defensive JSON parsing for messy real-world responses:
.get(), type validation, and arrays. - Chapter 7. API authentication with keys and tokens.
- Chapter 8. Coordinating multiple APIs in a single workflow.
- Chapter 9. Production-grade error handling with retries and backoff.
Everything that follows rests on the defensive-programming mindset you've built here. Before moving on, take half an hour and practise: build a downloader for a different file type like PDFs, add the size-limit challenge to your image downloader, write a function that downloads multiple files and reports success or failure for each, and spend a few minutes reading Content-Type headers from different APIs. The more you experiment now, the easier the chapters ahead will be.