Skip to main content
Advertisement

HTTP Requests

Master REST API calls, session management, and authentication patterns using requests and httpx.


Installation

pip install requests httpx

requests — Basic HTTP Client

import requests

# GET
response = requests.get("https://api.github.com/users/octocat")
print(response.status_code) # 200
print(response.json()) # dict
print(response.headers["content-type"])

# POST
data = {"username": "alice", "password": "secret"}
response = requests.post("https://httpbin.org/post", json=data)

# PUT / PATCH / DELETE
requests.put("https://api.example.com/items/1", json={"name": "Updated"})
requests.patch("https://api.example.com/items/1", json={"stock": 5})
requests.delete("https://api.example.com/items/1")

Request Parameters

# Query parameters
response = requests.get(
"https://api.example.com/products",
params={"page": 1, "limit": 20, "category": "books"},
)
# → https://api.example.com/products?page=1&limit=20&category=books

# Headers
headers = {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9...",
"Content-Type": "application/json",
"User-Agent": "MyApp/1.0",
}
response = requests.get("https://api.example.com/me", headers=headers)

# Timeout (always set this!)
response = requests.get("https://api.example.com/data",
timeout=(5, 30)) # (connect timeout, read timeout)

# File upload
with open("photo.jpg", "rb") as f:
response = requests.post(
"https://api.example.com/upload",
files={"file": ("photo.jpg", f, "image/jpeg")},
data={"description": "Profile photo"},
)

Session — Connection Reuse + Shared Config

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# Retry strategy
retry_strategy = Retry(
total=3, # max 3 retries
backoff_factor=1, # 1s, 2s, 4s exponential backoff
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry_strategy)

session = requests.Session()
session.mount("https://", adapter)
session.mount("http://", adapter)

# Shared headers
session.headers.update({
"Authorization": "Bearer my_token",
"Accept": "application/json",
})

# Requests using session (reuses connection, preserves cookies)
r1 = session.get("https://api.example.com/users")
r2 = session.get("https://api.example.com/products")

session.close() # or use context manager
with requests.Session() as s:
s.headers.update({"Authorization": "Bearer token"})
response = s.get("https://api.example.com/data")

Error Handling

import requests
from requests.exceptions import (
Timeout,
ConnectionError,
HTTPError,
RequestException,
)


def safe_request(url: str, **kwargs) -> dict | None:
try:
response = requests.get(url, timeout=10, **kwargs)
response.raise_for_status() # 4xx, 5xx → raises HTTPError
return response.json()
except Timeout:
print(f"Request timed out: {url}")
except ConnectionError:
print(f"Connection failed: {url}")
except HTTPError as e:
print(f"HTTP error {e.response.status_code}: {url}")
if e.response.status_code == 401:
print("Authentication required — check your token")
elif e.response.status_code == 429:
retry_after = e.response.headers.get("Retry-After", 60)
print(f"Retry after {retry_after}s")
except RequestException as e:
print(f"Request failed: {e}")
return None

httpx — Sync/Async Unified Client

import httpx

# Sync (similar API to requests)
with httpx.Client(timeout=10.0, follow_redirects=True) as client:
response = client.get("https://api.github.com/users/octocat")
data = response.json()

# Async (asyncio-based)
import asyncio


async def fetch_multiple(urls: list[str]) -> list[dict]:
async with httpx.AsyncClient(timeout=10.0) as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses if r.status_code == 200]


# Parallel API calls
async def get_user_data(user_ids: list[int]) -> list[dict]:
async with httpx.AsyncClient(
base_url="https://api.example.com",
headers={"Authorization": "Bearer token"},
timeout=15.0,
) as client:
tasks = [client.get(f"/users/{uid}") for uid in user_ids]
responses = await asyncio.gather(*tasks, return_exceptions=True)

results = []
for uid, resp in zip(user_ids, responses):
if isinstance(resp, Exception):
print(f"User {uid} fetch failed: {resp}")
elif resp.status_code == 200:
results.append(resp.json())
return results


# Run
user_ids = [1, 2, 3, 4, 5]
users = asyncio.run(get_user_data(user_ids))

Authentication Patterns

import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth


# 1. Basic Auth
response = requests.get(url, auth=HTTPBasicAuth("user", "pass"))
response = requests.get(url, auth=("user", "pass")) # shorthand

# 2. Bearer Token (JWT)
headers = {"Authorization": f"Bearer {access_token}"}
response = requests.get(url, headers=headers)

# 3. API Key
response = requests.get(url, headers={"X-API-Key": "my_api_key"})
response = requests.get(url, params={"api_key": "my_api_key"})

# 4. OAuth2 (automatic token refresh)
from requests_oauthlib import OAuth2Session # pip install requests-oauthlib

oauth = OAuth2Session(client_id, token=token)
response = oauth.get("https://api.example.com/resource")

REST API Client Class Pattern

import requests
from typing import Any


class APIClient:
def __init__(self, base_url: str, api_key: str):
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
self.base_url = base_url.rstrip("/")

def _request(self, method: str, path: str, **kwargs) -> Any:
url = f"{self.base_url}/{path.lstrip('/')}"
response = self.session.request(method, url, timeout=15, **kwargs)
response.raise_for_status()
return response.json() if response.content else None

def get(self, path: str, params: dict = None): return self._request("GET", path, params=params)
def post(self, path: str, data: dict): return self._request("POST", path, json=data)
def put(self, path: str, data: dict): return self._request("PUT", path, json=data)
def delete(self, path: str): return self._request("DELETE", path)

def __enter__(self): return self
def __exit__(self, *args): self.session.close()


# Usage
with APIClient("https://api.example.com", api_key="abc123") as client:
users = client.get("/users", params={"page": 1})
new_user = client.post("/users", data={"name": "Alice"})

Summary

Featurerequestshttpx
Sync requests
Async requests✅ (AsyncClient)
HTTP/2
Type hintspartial
RetriesHTTPAdapterhttpx.HTTPTransport

Use requests for simple synchronous calls, httpx when you need async or HTTP/2 support.

Advertisement