13.1. API Fundamentals: GET, Params, and Error Handling#
import requests
import json
13.1.1. What Is an API?#
An API (Application Programming Interface) is a defined way for two programs to communicate. A REST API (Representational State Transfer) is the most common style for web APIs:
The client (your code) sends an HTTP request to a URL called an endpoint
The server processes the request and sends back an HTTP response — usually JSON
Think of it like a restaurant menu: the menu lists what you can order (endpoints), you place an order (request), and the kitchen sends back your food (response).
13.1.1.1. HTTP Methods#
Method |
Typical use |
|---|---|
|
Retrieve data (read-only, safe to repeat) |
|
Create a new resource |
|
Update an existing resource |
|
Remove a resource |
13.1.1.2. HTTP Status Codes#
Code |
Meaning |
|---|---|
|
Request succeeded |
|
Resource was created |
|
Client sent invalid data |
|
Authentication required |
|
Endpoint or resource doesn’t exist |
|
Something went wrong on the server |
13.1.2. Making GET Requests with requests#
The requests library is the standard Python tool for HTTP. Install it with pip install requests.
response = requests.get(url)
response.status_code # integer status code (200, 404, …)
response.headers # dict of response headers
response.text # response body as a string
response.json() # response body parsed as JSON → dict or list
We’ll use httpbin.org — a free service that echoes back your request, perfect for learning.
# httpbin.org/get echoes back details about your GET request as JSON
response = requests.get('https://httpbin.org/get')
print("Status code:", response.status_code) # 200
print("Content-Type:", response.headers['Content-Type'])
# Parse the JSON body into a Python dict
data = response.json()
print("\nYour IP address:", data['origin'])
print("Request URL:", data['url'])
Status code: 200
Content-Type: application/json
Your IP address: 131.151.252.106
Request URL: https://httpbin.org/get
13.1.3. Query Parameters#
Most APIs accept configuration via query parameters — key/value pairs appended to the URL after ?. Instead of building the URL string manually, pass a params dict to requests.get():
# Manual: https://example.com/search?q=python&page=2
# With params dict (same result, cleaner):
requests.get('https://example.com/search', params={'q': 'python', 'page': 2})
# httpbin.org/get echoes back the params you send
params = {'course': 'IST5551', 'chapter': 15}
response = requests.get('https://httpbin.org/get', params=params)
data = response.json()
print("Full URL requested:", data['url'])
# → https://httpbin.org/get?course=IST5551&chapter=15
print("Args received by server:")
for key, value in data['args'].items():
print(f" {key} = {value}")
Full URL requested: https://httpbin.org/get?course=IST5551&chapter=15
Args received by server:
chapter = 15
course = IST5551
13.1.4. Error Handling#
Two levels of errors can occur:
Network error — no connection, timeout, DNS failure →
requestsraises an exceptionHTTP error — server responded but with a 4xx or 5xx status →
response.raise_for_status()raisesHTTPError
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # raises HTTPError for 4xx/5xx
data = response.json()
except requests.exceptions.Timeout:
print("Request timed out")
except requests.exceptions.HTTPError as e:
print(f"HTTP error {e.response.status_code}: {e}")
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
Always set a timeout — without one, your code can hang indefinitely.
def safe_get(url, params=None):
"""Make a GET request and return the JSON data, or None on failure."""
try:
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print(f"Timeout requesting {url}")
except requests.exceptions.HTTPError as e:
print(f"HTTP {e.response.status_code} error from {url}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
# Test with a valid URL
data = safe_get('https://httpbin.org/get', params={'test': 'ok'})
if data:
print("Success — status via raise_for_status passed")
print("Args:", data['args'])
# Test with an invalid URL (404)
bad = safe_get('https://httpbin.org/status/404')
print("404 result:", bad) # None
Success — status via raise_for_status passed
Args: {'test': 'ok'}
HTTP 404 error from https://httpbin.org/status/404
404 result: None
### Exercise: Weather API
# The Open-Meteo API (https://api.open-meteo.com) is free — no key required.
# Endpoint: https://api.open-meteo.com/v1/forecast
# Required query params:
# latitude=<float> longitude=<float> current=temperature_2m,wind_speed_10m
#
# 1. Use safe_get() to fetch current weather for New York (lat=40.71, lon=-74.01).
# 2. Extract and print the current temperature (°C) and wind speed (km/h).
# The values are inside data['current'].
# 3. Fetch weather for a second city of your choice and compare temperatures.
### Your code starts here.
BASE_URL = 'https://api.open-meteo.com/v1/forecast'
### Your code ends here.
### Solution
BASE_URL = 'https://api.open-meteo.com/v1/forecast'
cities = {
'New York': {'latitude': 40.71, 'longitude': -74.01},
'Los Angeles': {'latitude': 34.05, 'longitude': -118.24},
}
for city, coords in cities.items():
params = {**coords, 'current': 'temperature_2m,wind_speed_10m'}
data = safe_get(BASE_URL, params=params)
if data:
current = data['current']
temp = current['temperature_2m']
wind = current['wind_speed_10m']
print(f"{city}: {temp}°C, wind {wind} km/h")
New York: 20.2°C, wind 27.2 km/h
Los Angeles: 22.7°C, wind 14.5 km/h
13.1.5. Summary#
Concept |
Key idea |
|---|---|
REST API |
URL endpoints that accept HTTP requests and return data (usually JSON) |
|
Make a GET request; returns a |
|
Integer HTTP status (200 = OK, 404 = Not Found, …) |
|
Parse the JSON body into a Python dict or list |
|
Pass query parameters cleanly — |
|
Raises |
|
Always set a timeout to avoid hanging indefinitely |
|
Base class for all |