Handling Errors and Status Codes
- HTTP status codes communicate the outcome of an API request, and handling them correctly is key to robust automation.
- A simple
200 OK means success, while codes like 404 Not Found or 500 Internal Server Error indicate different failure modes.
- In this lecture, we’ll learn how to check status codes, use
response.ok, raise errors automatically, and inspect error details for troubleshooting.
Understanding HTTP Status Codes
- Status codes are grouped by their first digit: 1xx (informational), 2xx (success), 3xx (redirection), 4xx (client error), 5xx (server error).
- Examples include
200 OK, 201 Created, 301 Moved Permanently, 404 Not Found, and 500 Internal Server Error.
- Knowing these categories helps you decide how to handle each response in your scripts.
Checking response.status_code
- After a
requests call, the integer response.status_code tells you the exact HTTP code returned.
- You can compare it directly (e.g.,
if resp.status_code == 404:) to implement custom logic based on the code.
- This explicit check is useful when you need fine-grained control over specific status codes.
GITHUB_ENDPOINT = "https://api.github.com"
HTTPBIN_ENDPOINT = "https://httpbin.org"
import requests
urls = {
"ok": f"{GITHUB_ENDPOINT}/zen",
"not_found": f"{GITHUB_ENDPOINT}/nonexistentendpoint"
}
for description, url in urls.items():
response = requests.get(url, timeout=5)
print(f"{description}: status {response.status_code}")
Using response.ok
- The boolean
response.ok is True for any status code below 400 (1xx, 2xx, 3xx) and False for 4xx/5xx errors.
- This provides a quick success/failure check without examining the numeric code directly.
- It’s a handy shorthand when you only need to know if the request broadly succeeded.
import requests
urls = {
"ok": f"{GITHUB_ENDPOINT}/zen",
"not_found": f"{GITHUB_ENDPOINT}/nonexistentendpoint"
}
for description, url in urls.items():
response = requests.get(url, timeout=5)
print(f"{description}: ok? {"Yes" if response.ok else f"No. Failed with status {response.status_code}"}")
Automatic Error Raising with raise_for_status()
- Calling
response.raise_for_status() will do nothing on 1xx, 2xx and 3xx codes but raise an HTTPError on 4xx/5xx.
- This follows the EAFP (“Easier to Ask Forgiveness than Permission”) style: try the request, and catch errors if they occur.
- The caught exception carries the original
response in its response attribute, letting you inspect headers and body.
import requests
import json
urls = {
"ok": f"{GITHUB_ENDPOINT}/zen",
"not_found": f"{GITHUB_ENDPOINT}/nonexistentendpoint"
}
for url in urls.values():
print(f"Requesting: {url}")
try:
res = requests.get(url, timeout=5)
res.raise_for_status()
print(" Success!")
except requests.exceptions.HTTPError as err:
print(f" HTTPError: {err} (status {err.response.status_code})")
try:
details = err.response.json()
print(" Error details:")
print(json.dumps(details, indent=2))
except ValueError:
print(f" Non-JSON response body: {err.response.text[:100]}")
Common Pitfalls & How to Avoid Them
- Not checking errors: Treating any response as success can mask failures. Always use
ok or raise_for_status().
- Catching too broadly: A generic
except Exception: hides HTTP errors. Catch HTTPError specifically.
- Ignoring error bodies: APIs often return JSON error messages; inspect
response.text or response.json().