Python · API · Advanced
Error Handling
Handle HTTP errors, network failures, and timeouts robustly in API scripts.
Quick topic start and explanations before exercises (exercises below):
Practical error handling patterns
#Error handling reference
#Exercises:
Catch a 404 with raise_for_status
#Try to GET /api/products/99999/ (a non-existent product). Use raise_for_status() inside a try/except to catch the HTTPError and print the status code.
import requests BASE_URL = 'https://apilearn.tukas.dev' # GET a non-existent product and handle the HTTPError
Solution
import requests
BASE_URL = 'https://apilearn.tukas.dev'
try:
response = requests.get(f'{BASE_URL}/api/products/99999/')
response.raise_for_status()
print(response.json())
except requests.exceptions.HTTPError as e:
print(f'HTTP error: {e.response.status_code}')
Write safe_get()
#Write a safe_get(url) function that returns the parsed JSON on success or None on any error (HTTP or network). Test it with a valid URL and with /api/products/99999/.
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def safe_get(url):
# Return JSON on success, None on any error
pass
print(safe_get(f'{BASE_URL}/api/products/') is not None) # True
print(safe_get(f'{BASE_URL}/api/products/99999/')) # None
Solution
import requests
BASE_URL = 'https://apilearn.tukas.dev'
def safe_get(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException:
return None
print(safe_get(f'{BASE_URL}/api/products/') is not None)
print(safe_get(f'{BASE_URL}/api/products/99999/'))
Handle timeout and network errors
#Make a GET to /api/products/ with timeout=5. Handle Timeout and ConnectionError separately. Print the product list on success or an error message for each exception type.
import requests BASE_URL = 'https://apilearn.tukas.dev' # GET with timeout and network error handling
Solution
import requests
BASE_URL = 'https://apilearn.tukas.dev'
try:
response = requests.get(f'{BASE_URL}/api/products/', timeout=5)
response.raise_for_status()
products = response.json()['results']
print(f'Got {len(products)} products')
except requests.exceptions.Timeout:
print('Error: request timed out')
except requests.exceptions.ConnectionError:
print('Error: could not connect to API')
except requests.exceptions.HTTPError as e:
print(f'HTTP error: {e.response.status_code}')
Write retry_get()
#Write retry_get(url, retries=3, delay=1) that retries a failed request up to retries times, waiting delay * attempt seconds between tries. Return the JSON on success or None if all retries fail. Test it with a valid URL.
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, delay=1):
# Try up to retries times, sleeping delay*attempt seconds between tries
pass
data = retry_get(f'{BASE_URL}/api/products/')
print(data['count'] if data else 'Failed')
Solution
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, delay=1):
for attempt in range(1, retries + 1):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f'Attempt {attempt} failed: {e}')
if attempt < retries:
time.sleep(delay * attempt)
return None
data = retry_get(f'{BASE_URL}/api/products/')
print(data['count'] if data else 'Failed')
Retry with backoff
#Write a retry_get(url, retries=3) function that retries on 429 or 503 status codes, waiting 2**attempt seconds between tries. For other errors raise immediately. Print a message before each wait.
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, **kwargs):
# Retry on 429/503, exponential backoff, raise on other errors
pass
# Test with a working endpoint (does not trigger retry)
result = retry_get(f'{BASE_URL}/api/products/')
print(result['count'])
Solution
import requests
import time
BASE_URL = 'https://apilearn.tukas.dev'
def retry_get(url, retries=3, **kwargs):
for attempt in range(retries):
try:
response = requests.get(url, timeout=5, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code in (429, 503) and attempt < retries - 1:
wait = 2 ** attempt
print(f'Attempt {attempt + 1} failed ({e.response.status_code}), waiting {wait}s...')
time.sleep(wait)
else:
raise
return None
result = retry_get(f'{BASE_URL}/api/products/')
print(result['count'])