Python
error-handling
try-except
requests-module
programming-tips

Correct way to try/except using Python requests module?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The right way to use try and except with requests is to catch the specific network exceptions you can actually handle, set a timeout, and call raise_for_status() if HTTP error codes should count as failures. Most bad requests code fails either by catching too broadly or by forgetting that a 404 response does not raise an exception by default.

The Basic Pattern

A solid starting point looks like this:

python
1import requests
2
3
4def fetch_json(url: str) -> dict:
5    try:
6        response = requests.get(url, timeout=5)
7        response.raise_for_status()
8        return response.json()
9    except requests.exceptions.Timeout:
10        print("request timed out")
11        raise
12    except requests.exceptions.HTTPError as error:
13        print(f"http error: {error.response.status_code}")
14        raise
15    except requests.exceptions.RequestException as error:
16        print(f"request failed: {error}")
17        raise
18
19
20print(fetch_json("https://httpbin.org/json"))

This pattern does three important things:

  • it sets timeout=5,
  • it converts bad HTTP status codes into exceptions with raise_for_status(),
  • it catches specific requests exceptions before the general base class.

Why RequestException Matters

requests.exceptions.RequestException is the base class for the major errors raised by the library, including:

  • 'ConnectionError,'
  • 'Timeout,'
  • 'HTTPError,'
  • 'TooManyRedirects.'

If you only need one fallback handler, catching RequestException is reasonable. If you need different behavior for retryable failures versus permanent ones, catch the narrower exceptions first.

Do Not Swallow Errors Silently

This is the pattern to avoid:

python
1import requests
2
3try:
4    response = requests.get("https://example.com")
5except Exception:
6    pass

It has several problems:

  • it catches far more than request-related failures,
  • it hides useful debugging information,
  • it leaves the caller with no idea whether the request succeeded.

If you catch an exception, either recover meaningfully, return a clear fallback value, or re-raise it.

Handling HTTP Errors Properly

A successful TCP connection is not the same as a successful application response. For example, 404 and 500 still return a Response object.

That means this code does not fail automatically:

python
response = requests.get("https://httpbin.org/status/404", timeout=5)
print(response.status_code)

If your logic treats non-2xx responses as errors, call raise_for_status().

python
response = requests.get("https://httpbin.org/status/404", timeout=5)
response.raise_for_status()

Now an HTTPError is raised, which you can catch explicitly.

Returning Fallback Values

Sometimes the caller does not want an exception. In that case, convert the failure into an explicit return value.

python
1import requests
2
3
4def fetch_text_or_none(url: str) -> str | None:
5    try:
6        response = requests.get(url, timeout=3)
7        response.raise_for_status()
8        return response.text
9    except requests.exceptions.RequestException as error:
10        print(f"download failed: {error}")
11        return None

The important thing is that the function contract stays clear. Either it raises, or it returns a defined fallback.

Sessions and Repeated Requests

If you make many requests, a Session is cleaner and more efficient than calling requests.get repeatedly.

python
1import requests
2
3session = requests.Session()
4session.headers.update({"User-Agent": "example-client/1.0"})
5
6try:
7    response = session.get("https://httpbin.org/get", timeout=5)
8    response.raise_for_status()
9    print(response.json())
10except requests.exceptions.RequestException as error:
11    print(f"request failed: {error}")
12finally:
13    session.close()

This is especially useful when you share headers, cookies, or connection pooling.

Common Pitfalls

The most common mistake is forgetting the timeout. Without it, a request can hang much longer than you expect.

Another issue is catching Exception instead of requests.exceptions.RequestException. That hides unrelated bugs and makes debugging harder.

A third problem is assuming requests will raise for 404 or 500 automatically. It will not unless you call raise_for_status().

Summary

  • Catch requests exceptions, not broad Exception, unless you have a very specific reason.
  • Always set a timeout for network calls.
  • Use raise_for_status() when HTTP error codes should count as failures.
  • Catch narrow exceptions first, then RequestException as the general fallback.
  • Either re-raise the error or return a well-defined fallback value.

Course illustration
Course illustration

All Rights Reserved.