Skip to main content

Python HTTP Requests

Mentor's Note: The requests library 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
payload = {"username": "alice", "email": "[email protected]"}
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",
"email": "[email protected]",
"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

StepCodeResult
1response = requests.get("https://api.github.com/users/octocat")Connection established
2response.status_code200
3response.okTrue
4data = response.json()Python dict with 30+ keys
5data["login"]"octocat"
6data["public_repos"]8
7Error 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 Session for performance when making many requests to the same server.
  • response.json() can raise ValueError if 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."


← Back: Async/Await | Next: Packaging →