APIs and REST for Network Engineers
Handling Errors, Timeouts, and Status Codes
What you'll learn
- How to check a status code and react correctly to 4xx/5xx
- How to catch a real request timeout without the script hanging or crashing
- How to catch a connection error when a target isn't reachable at all
- That HTTP success and business/data success are two separate questions
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
Status codes, checked deliberately
response = requests.get("http://127.0.0.1:8765/devices/SW99/interfaces")
print(response.status_code)
try:
response.raise_for_status()
print("no error raised (unexpected)")
except requests.exceptions.HTTPError as e:
print(type(e).__name__, "-", e)raise_for_status() turns a 4xx or 5xx response into a real Python exception — an explicit opt-in check, not automatic. Calling it is what actually enforces 'don't trust a bad response.'
404
HTTPError - 404 Client Error: Not Found for url: http://127.0.0.1:8765/devices/SW99/interfacesReal, verified output — requesting an unknown device against this module's mock API. Without calling raise_for_status(), this 404 would just sit in response.status_code, easy to forget to check.
Two different failures: timeout and connection error
try:
requests.get("http://127.0.0.1:8765/devices/SW4/vlans", timeout=1)
except requests.exceptions.Timeout as e:
print("Timeout:", e)timeout=1 tells requests to give up after 1 second of waiting. This module's mock API deliberately makes SW4 respond slowly — a real, reproducible way to trigger this exception, not a hypothetical.
Timeout: HTTPConnectionPool(host='127.0.0.1', port=8765): Read timed out. (read timeout=1)Real, verified output — raised after almost exactly 1 second, matching the timeout= value. requests.exceptions.Timeout covers both a slow connection and a slow response.
Always set timeout= explicitly
Without timeout=, requests will wait indefinitely for a response that may never come — a script that looks like it's just running, when it's actually stuck forever. This is the HTTP equivalent of Netmiko's own conn_timeout — a required habit, not an optional nicety.
try:
requests.get("http://127.0.0.1:9999/devices/SW1/interfaces", timeout=1)
except requests.exceptions.ConnectionError as e:
print(type(e).__name__)A different exception for a different problem: nothing is listening at all on port 9999, so the connection itself never gets established — distinct from a timeout, where a connection succeeds but the response never arrives in time.
ConnectTimeoutReal, verified output. ConnectTimeout is one of requests' more specific exceptions, and it's also a ConnectionError — catching requests.exceptions.ConnectionError (as shown above) catches it, along with every other connection-level failure.
HTTP success is not the same claim as data success
response = requests.get("http://127.0.0.1:8765/devices/SW2/vlans", timeout=2)
print("HTTP succeeded:", response.status_code == 200)
vlans = response.json()["vlans"]
required = [{"id": 10, "name": "USERS"}]
missing = [v for v in required if v not in vlans]
print("but required VLANs missing:", missing)SW2's request returns a perfectly healthy 200 — the HTTP layer has nothing to complain about. Whether the DATA inside that response is what's actually required is an entirely separate check, run afterward.
HTTP succeeded: True
but required VLANs missing: [{'id': 10, 'name': 'USERS'}]Real, verified output. A 200 status code only proves the request was received and answered — it says nothing about whether the answer is the one you needed.
Exactly the theme from every capstone before this one
'Ran without error' was never the same claim as 'succeeded' — Reliable Automation's post-check, Configuration & Template Automation's rendered-config validation, and Ansible's check-mode all drew this same line. Here it's a status code that can mislead if trusted alone: 200 only means the HTTP layer is happy. This module's capstone (Lesson 7) checks both questions, in that order, on every device.
Key takeaways
- raise_for_status() turns a 4xx/5xx response into a real exception — an explicit choice, not automatic behavior.
- requests.exceptions.Timeout (a slow response) and requests.exceptions.ConnectionError (no connection at all) are two distinct, real failure modes — both verified directly against this module's own mock API.
- timeout= is required, not optional — without it, a request can hang indefinitely, the HTTP equivalent of Netmiko's conn_timeout.
- A 200 status code proves the HTTP layer succeeded — it says nothing about whether the response's actual data is correct. Checking both, in order, is exactly what this module's capstone does for every device.