Python HTTP Requests
Python HTTP Requests is a core Python concept covering learn how to make HTTP requests in Python using the requests library. Master GET, POST, headers, error handling, and working with REST APIs. This topic is essential for academic learning, board exam preparation, and developing optimized real-world code.
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."