Python HTTP Requests
Mentor's Note: The
requestslibrary is your passport to the internet. With a few lines of Python, you can talk to any API on the planet. 🌐
The Scenario: The Food Delivery App
Imagine you're building a food delivery app that shows weather-based recommendations.
- The Need: You need weather data from a remote server — you don't have a weather station on your roof! 📡
- The Solution: Your app sends an HTTP request to a weather API, and the server sends back a response with the data. This is how all modern apps talk to each other.
- The Result: Your app shows "Rainy day — 20% off hot soup! ☕" — all powered by a single HTTP request. ✅
What Is the Requests Library?
requests is the de-facto Python library for making HTTP requests. It provides a clean, human-friendly API for calling REST APIs and fetching web resources.
pip install requests
GET Requests
The most common HTTP method — fetch data from a server.
import requests
# Basic GET request
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
# Status code
print(response.status_code) # 200
# Response body as text
print(response.text) # Raw JSON string
# Response body as Python dict
data = response.json() # Parsed JSON -> dict
print(data["title"]) # Access fields
Response Object Breakdown
import requests
response = requests.get("https://api.github.com")
print(response.status_code) # 200 (success)
print(response.ok) # True (status < 400)
print(response.headers) # Dict of response headers
print(response.elapsed) # Time taken (timedelta)
print(response.encoding) # Character encoding (utf-8)
print(response.url) # Final URL (after redirects)
POST Requests
Send data to a server — create a new resource.
import requests
# Send form-encoded data
response = requests.post("https://httpbin.org/post", data=payload)
print(response.json())
# Send JSON data
json_payload = {"title": "Hello", "body": "World", "userId": 1}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=json_payload
)
print(response.status_code) # 201 (Created)
print(response.json()) # The created resource with ID
Headers and Params
Custom Headers
Many APIs require headers for authentication, content type, or metadata.
import requests
headers = {
"Authorization": "Bearer your_token_here",
"Accept": "application/json",
"User-Agent": "MyApp/1.0"
}
response = requests.get(
"https://api.github.com/user",
headers=headers
)
print(response.json())
Query Parameters
Append parameters to the URL cleanly:
import requests
params = {
"q": "python requests",
"sort": "stars",
"per_page": 5
}
response = requests.get(
"https://api.github.com/search/repositories",
params=params
)
data = response.json()
for repo in data["items"]:
print(f"{repo['full_name']}: {repo['stargazers_count']} ⭐")
Error Handling
Network requests can fail in many ways — always handle exceptions.
import requests
from requests import exceptions
url = "https://api.example.com/data"
try:
response = requests.get(url, timeout=5)
# Raise an error for 4xx/5xx status codes
response.raise_for_status()
data = response.json()
print(data)
except exceptions.Timeout:
print(f"Request to {url} timed out.")
except exceptions.ConnectionError:
print(f"Could not connect to {url}.")
except exceptions.HTTPError as e:
print(f"HTTP error: {e.response.status_code} - {e.response.reason}")
except exceptions.RequestException as e:
print(f"An unexpected error occurred: {e}")
Real Example: Weather API
Fetch current weather from a free API (Open-Meteo — no API key required):
import requests
def get_weather(latitude: float, longitude: float) -> dict:
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": latitude,
"longitude": longitude,
"current_weather": True,
"timezone": "auto"
}
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Weather fetch failed: {e}")
return {}
# New York City
weather = get_weather(40.7128, -74.0060)
if weather:
current = weather["current_weather"]
temp = current["temperature"]
wind = current["windspeed"]
condition = current["weathercode"]
print(f"NYC: {temp}°C, Wind: {wind} km/h, Code: {condition}")
Authentication: API Keys
Most APIs require authentication via an API key in the headers.
import requests
API_KEY = "your_api_key_here"
headers = {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
}
# Option 1: Header-based
response = requests.get(
"https://api.example.com/data",
headers=headers
)
# Option 2: Query parameter-based
response = requests.get(
"https://api.example.com/data",
params={"api_key": API_KEY}
)
# Option 3: Basic auth
from requests.auth import HTTPBasicAuth
response = requests.get(
"https://api.example.com/secure",
auth=HTTPBasicAuth("username", "password")
)
Visual Logic: HTTP Request Flow
Working with JSON APIs
Most modern APIs return JSON. response.json() parses it into Python data structures.
import requests
# Fetch user data
response = requests.get("https://jsonplaceholder.typicode.com/users")
users = response.json() # List of dicts
for user in users:
name = user["name"]
email = user["email"]
city = user["address"]["city"]
print(f"{name:20s} | {email:25s} | {city}")
# POST a new user
new_user = {
"name": "Vishnu",
"address": {"city": "Mumbai"}
}
response = requests.post(
"https://jsonplaceholder.typicode.com/users",
json=new_user
)
print(f"Created user with ID: {response.json()['id']}")
Session Objects
For multiple requests to the same server, use a Session — it reuses the TCP connection and persists headers.
import requests
# Create a session with default headers
session = requests.Session()
session.headers.update({
"User-Agent": "MyApp/1.0",
"Accept": "application/json"
})
# All requests share the session
resp1 = session.get("https://api.github.com/users/octocat")
resp2 = session.get("https://api.github.com/users/octocat/repos")
print(resp1.json()["login"])
print(f"Public repos: {len(resp2.json())}")
session.close()
Sample Dry Run
Scenario: Fetch a GitHub user profile
| Step | Code | Result |
|---|---|---|
| 1 | response = requests.get("https://api.github.com/users/octocat") | Connection established |
| 2 | response.status_code | 200 ✅ |
| 3 | response.ok | True |
| 4 | data = response.json() | Python dict with 30+ keys |
| 5 | data["login"] | "octocat" |
| 6 | data["public_repos"] | 8 |
| 7 | Error scenario: requests.get("https://bad.url") | ConnectionError ❌ |
Pro Tips
- Always set a
timeout— otherwise your program could hang forever. - Use
raise_for_status()to catch HTTP errors cleanly. - Use
Sessionfor performance when making many requests to the same server. response.json()can raiseValueErrorif the response is not valid JSON — wrap in try/except.
Interview Tip
"Interviewers ask: 'What's the difference between PUT and PATCH?' PUT replaces the entire resource; PATCH applies a partial update. PUT is idempotent — calling it multiple times produces the same result."