Handling API Responses and Errors
Parsing JSON responses, error codes, and exception handling patterns.
Handling API Responses and Errors is a free AI Agents lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Agents learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Response Object
Every requests call returns a Response object. It contains everything the server sent back: the status code, headers, and body. Before processing the body, always inspect the status code — a response with a 500 status still has a body, but it won't contain the data you wanted.
import requests
response = requests.get('https://api.example.com/data')
# Key attributes of the response
print(response.status_code) # e.g. 200
print(response.headers) # dict of response headers
print(response.headers.get('Content-Type')) # 'application/json'
print(response.text) # raw response body as string
print(response.content) # raw bytesParsing JSON with response.json()
Call response.json() to automatically parse the response body as JSON into a Python dict or list. This is equivalent to json.loads(response.text), but also validates that the Content-Type is appropriate.
Only call .json() when you know the response is actually JSON — check the Content-Type header first.
import requests
response = requests.get(
'https://api.example.com/users/42',
headers={'Authorization': 'Bearer YOUR_KEY'}
)
# Parse JSON body
user = response.json()
# Access fields safely with .get()
name = user.get('name', 'Unknown')
email = user.get('email', '')
roles = user.get('roles', [])
print(f'User: {name} ({email})')
print(f'Roles: {roles}')Checking status_code Before Parsing
Never call response.json() without first confirming the request succeeded. Error responses (4xx/5xx) often return JSON error details — useful for debugging — but they're not the data you need. Always check status_code first.
import requests
response = requests.post(
'https://api.example.com/tasks',
json={'title': 'Write report'},
headers={'Authorization': 'Bearer YOUR_KEY'}
)
if response.status_code == 201:
task = response.json()
print('Task created, ID:', task['id'])
elif response.status_code == 400:
error = response.json()
print('Validation error:', error.get('message'))
elif response.status_code == 401:
print('Auth failed — check your token')
else:
print(f'Unexpected status {response.status_code}: {response.text[:200]}')raise_for_status() — Automatic Error Raising
response.raise_for_status() raises an HTTPError exception automatically if the status code is 4xx or 5xx. This is a clean way to convert bad HTTP responses into Python exceptions, letting you use try/except instead of long if/elif chains.
import requests
from requests.exceptions import HTTPError
try:
response = requests.get(
'https://api.example.com/users/9999',
headers={'Authorization': 'Bearer YOUR_KEY'}
)
response.raise_for_status() # raises if status >= 400
user = response.json()
print('Found user:', user['name'])
except HTTPError as e:
print(f'HTTP error: {e.response.status_code}')
print('Details:', e.response.text[:300])Handling JSONDecodeError
Sometimes an API returns a non-JSON response when you expect JSON — a server error page in HTML, an empty body, or a binary file. Calling response.json() on these raises json.JSONDecodeError. Always catch this to avoid silent agent crashes.
import requests
import json
response = requests.get(
'https://api.example.com/report',
headers={'Authorization': 'Bearer YOUR_KEY'}
)
try:
data = response.json()
except json.JSONDecodeError as e:
print(f'Response is not valid JSON: {e}')
print('Content-Type:', response.headers.get('Content-Type'))
print('First 200 chars:', response.text[:200])
# Decide: is this an HTML error page? A CSV file?
data = None
if data is None:
print('Falling back to text processing')ConnectionError — Network Problems
A ConnectionError happens when your agent can't reach the server at all — DNS resolution failure, server offline, firewall blocking the request. It's a network-level failure before any HTTP even happens.
Unlike a 5xx, this isn't the server's response — the connection never happened.
import requests
from requests.exceptions import ConnectionError
try:
response = requests.get('https://api.example.com/data')
data = response.json()
except ConnectionError as e:
print('Cannot reach server. Possible causes:')
print('- DNS failure (bad hostname)')
print('- Server is down')
print('- No internet connection')
print('- Firewall blocking the port')
print(f'Error detail: {e}')
# Consider: queue the request for retry when connectivity returnsTimeout — Preventing Stuck Agents
By default, requests waits forever for a response. A slow or hanging server will freeze your agent indefinitely. Always set a timeout: a tuple of (connect_timeout, read_timeout) in seconds. A Timeout exception is raised if the server doesn't respond in time.
import requests
from requests.exceptions import Timeout
try:
response = requests.get(
'https://api.example.com/slow-endpoint',
headers={'Authorization': 'Bearer YOUR_KEY'},
timeout=(5, 30) # 5s to connect, 30s to read
)
data = response.json()
except Timeout:
print('Request timed out after 30 seconds')
print('Options: retry, use cached result, or alert operator')Comprehensive Exception Handling
In production agents, catch all requests exceptions in a consistent hierarchy. requests.exceptions.RequestException is the base class for all requests errors — catching it gives you a safety net for unexpected network issues.
import requests
import json
from requests.exceptions import (
ConnectionError, Timeout, HTTPError, RequestException
)
def safe_api_call(url, headers):
try:
r = requests.get(url, headers=headers, timeout=(5, 30))
r.raise_for_status()
return r.json()
except Timeout:
print('ERROR: Request timed out')
except ConnectionError:
print('ERROR: Cannot reach server')
except HTTPError as e:
print(f'ERROR: HTTP {e.response.status_code}')
try:
print('API error:', e.response.json().get('message'))
except json.JSONDecodeError:
print('Non-JSON error body')
except RequestException as e:
print(f'ERROR: Unexpected request error: {e}')
return NoneLogging Responses for Debugging
When an agent misbehaves, you need enough context to diagnose it. Log the request method, URL, status code, and relevant response details — but never log API keys. Use Python's built-in logging module rather than print statements for production agents.
import logging
import requests
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('agent.api')
def logged_request(method, url, **kwargs):
logger.info(f'-> {method.upper()} {url}')
response = requests.request(method, url, **kwargs)
logger.info(
f'<- {response.status_code} '
f'({len(response.content)} bytes) '
f'{response.elapsed.total_seconds():.2f}s'
)
if response.status_code >= 400:
logger.error(f'Error body: {response.text[:500]}')
return responseHandling Paginated Responses
Many APIs return data in pages. Your agent must follow pagination links to get all results. Look for a next URL in the response or a page/cursor field, and loop until there are no more pages.
import requests
def get_all_items(base_url, headers):
all_items = []
url = f'{base_url}/items?page=1&limit=100'
while url:
response = requests.get(url, headers=headers)
response.raise_for_status()
data = response.json()
all_items.extend(data.get('items', []))
# Follow 'next' link if present
url = data.get('next_page_url') # None stops the loop
print(f'Fetched {len(all_items)} items so far...')
print(f'Total: {len(all_items)} items')
return all_itemsStreaming Large Responses
For large responses (files, long AI outputs), use stream=True to avoid loading the entire response into memory at once. Read the response in chunks. This is essential when your agent processes large datasets or streams AI-generated text.
import requests
response = requests.get(
'https://api.example.com/large-report',
headers={'Authorization': 'Bearer YOUR_KEY'},
stream=True
)
response.raise_for_status()
# Write streamed content to file
with open('report.json', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print('Download complete')
# For streaming JSON lines (NDJSON):
for line in response.iter_lines():
if line:
import json
record = json.loads(line)
print(record)Quick Check: raise_for_status
Test your understanding of response error handling.
Response Handling Recap
Robust response handling is what separates a brittle agent from a reliable one:
- Always check
status_codebefore parsing the body - Use
response.json()to parse, catchJSONDecodeErrorif body might not be JSON - Use
raise_for_status()to convert HTTP errors to exceptions - Catch
ConnectionErrorfor network failures andTimeoutfor slow servers - Always set a
timeout=(connect, read)tuple on every request - Log requests and responses (without keys) for debuggability
Frequently asked questions
Is the “Handling API Responses and Errors” lesson free?
Yes — the full text of “Handling API Responses and Errors” is free to read here on the web, and the AI Agents course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Agents course, upgrade to CoddyKit PRO.
What will I learn in “Handling API Responses and Errors”?
Parsing JSON responses, error codes, and exception handling patterns. You practise AI Agents with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start AI Agents?
No prior experience is required. AI Agents on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Handling API Responses and Errors” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this AI Agents lesson?
Yes. Every AI Agents lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- REST API Fundamentals for Agent Developers
- Authentication: API Keys and OAuth
- Handling API Responses and Errors
- Rate Limiting and Retry Logic