The Python Developer's Guide to APIs: Requests Library Deep Dive
โก Quick Answer
A deep dive into Python's requests library: making API calls, handling authentication, error handling, sessions, and advanced patterns every developer needs.
Editorial note: This page is educational. Time-sensitive claims, comparisons, and reviews should include the supporting sources and update information in the article.
Read our editorial policy โGet more content like this on Telegram!
Daily AI tips, notes & resources free
The Python Developer's Guide to APIs: Requests Library Deep Dive
requests is Python's standard library for making HTTP calls, the layer that turns "talk to a web server" into a few readable lines instead of raw socket handling.
Every modern Python project calls an API somewhere: weather data, user authentication, payment processing, social media integration. It's all HTTP requests under the hood, and requests makes them clean and intuitive.
But there's a big gap between the basic requests.get(url) tutorial example and the production-ready API client code real projects need, timeouts, retries, pagination, error handling. This guide closes that gap.
Installation and Basic Usage
pip install requestsimport requests
response = requests.get("https://api.github.com/users/gvanrossum")
print(response.status_code) # 200
print(response.json()) # Python dict from JSON responseThe response object has everything you need:
response.status_code # 200, 404, 500, etc.
response.text # Raw text response
response.json() # Parse JSON โ Python dict/list
response.headers # Response headers dict
response.url # Final URL (after redirects)
response.content # Raw bytes (for images/files)
response.elapsed # Time taken for the requestThe HTTP Methods
# GET โ retrieve data
response = requests.get("https://api.example.com/users")
# POST โ create data
response = requests.post("https://api.example.com/users",
json={"name": "Alice", "email": "alice@example.com"})
# PUT โ replace data entirely
response = requests.put("https://api.example.com/users/1",
json={"name": "Alice Updated", "email": "alice@example.com"})
# PATCH โ update specific fields
response = requests.patch("https://api.example.com/users/1",
json={"name": "Alice Updated"})
# DELETE โ remove data
response = requests.delete("https://api.example.com/users/1")Sending Data
Query Parameters (URL Parameters)
# Manual: requests.get("https://api.example.com/search?q=python&limit=10")
# Clean way:
params = {"q": "python", "limit": 10, "sort": "stars"}
response = requests.get("https://api.github.com/search/repositories",
params=params)
print(response.url) # Shows the full URL with encoded paramsJSON Body (Most Common for POST/PUT)
data = {
"title": "Learn Python",
"completed": False,
"priority": "high"
}
response = requests.post("https://jsonplaceholder.typicode.com/todos",
json=data) # Automatically serializes to JSON
# Sets Content-Type: application/jsonForm Data
# For HTML form submissions (Content-Type: application/x-www-form-urlencoded)
response = requests.post("https://example.com/login",
data={"username": "alice", "password": "secret"})File Upload
with open("report.pdf", "rb") as f:
response = requests.post("https://api.example.com/upload",
files={"file": ("report.pdf", f, "application/pdf")},
headers={"Authorization": "Bearer YOUR_TOKEN"})Authentication
API Key in Header
import os
API_KEY = os.environ["API_KEY"] # Never hardcode!
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.get("https://api.example.com/data", headers=headers)API Key as Query Parameter
response = requests.get("https://api.weathermap.org/data",
params={"appid": API_KEY, "q": "London"})HTTP Basic Auth
from requests.auth import HTTPBasicAuth
response = requests.get("https://api.example.com/private",
auth=HTTPBasicAuth("username", "password"))
# Shorthand:
response = requests.get(url, auth=("username", "password"))Error Handling, Production Patterns
import requests
from requests.exceptions import HTTPError, ConnectionError, Timeout, RequestException
def api_get(url: str, **kwargs) -> dict:
try:
response = requests.get(url, timeout=10, **kwargs)
response.raise_for_status() # Raises HTTPError for 4xx/5xx
return response.json()
except HTTPError as e:
status = e.response.status_code
if status == 404:
raise ValueError(f"Resource not found: {url}")
elif status == 401:
raise PermissionError("Authentication failed")
elif status == 429:
raise RuntimeError("Rate limit exceeded โ slow down requests")
else:
raise RuntimeError(f"HTTP error {status}: {e}")
except ConnectionError:
raise RuntimeError(f"Cannot connect to {url}")
except Timeout:
raise RuntimeError(f"Request timed out after 10s: {url}")
except RequestException as e:
raise RuntimeError(f"Request failed: {e}")Always set timeout. Without it, a hanging server hangs your program indefinitely, there is no default, so an unresponsive API can freeze a script forever.
Sessions, Efficient Multi-Request Patterns
A Session persists headers, auth, and cookies across requests and reuses TCP connections, like keeping one phone line open for a conversation instead of hanging up and redialing before every sentence.
import requests
class GitHubClient:
BASE_URL = "https://api.github.com"
def __init__(self, token: str):
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
})
def get_user(self, username: str) -> dict:
response = self.session.get(f"{self.BASE_URL}/users/{username}",
timeout=10)
response.raise_for_status()
return response.json()
def get_repos(self, username: str) -> list:
response = self.session.get(f"{self.BASE_URL}/users/{username}/repos",
params={"per_page": 100, "sort": "updated"},
timeout=10)
response.raise_for_status()
return response.json()
def close(self):
self.session.close()
# Usage
client = GitHubClient(token=os.environ["GITHUB_TOKEN"])
user = client.get_user("gvanrossum")
repos = client.get_repos("gvanrossum")
print(f"{user['name']} has {len(repos)} repos")
client.close()Retry Logic
For production API clients, add automatic retries with exponential backoff:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session_with_retries() -> requests.Session:
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1, # 1s, 2s, 4s between retries
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
session = create_session_with_retries()
response = session.get("https://api.example.com/data", timeout=10)Pagination Patterns
Most APIs paginate large result sets. Common patterns:
Offset Pagination
def get_all_users(base_url: str, headers: dict) -> list[dict]:
all_users = []
page = 1
while True:
response = requests.get(base_url,
params={"page": page, "per_page": 100},
headers=headers,
timeout=10)
response.raise_for_status()
data = response.json()
if not data:
break
all_users.extend(data)
page += 1
return all_usersCursor Pagination
def get_all_records(url: str, headers: dict) -> list[dict]:
records = []
next_url = url
while next_url:
response = requests.get(next_url, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
records.extend(data["results"])
next_url = data.get("next") # None when no more pages
return recordsCalling the Claude API
A real example calling the Anthropic API with requests:
import os
import requests
def ask_claude(prompt: str, model: str = "claude-sonnet-4-6") -> str:
response = requests.post(
"https://api.anthropic.com/v1/messages",
headers={
"x-api-key": os.environ["ANTHROPIC_API_KEY"],
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json={
"model": model,
"max_tokens": 1024,
"messages": [{"role": "user", "content": prompt}]
},
timeout=30
)
response.raise_for_status()
return response.json()["content"][0]["text"]
answer = ask_claude("Explain Python decorators in 3 sentences")
print(answer)The pattern is identical to any other API: headers for auth, json= for the body, raise_for_status() for errors, and a timeout that matches how long the endpoint is allowed to take.
Further Reading
- Python Decorators and Generators, Advanced Python Made Simple 2026
- Python File Handling Guide: Read, Write, Process Any File Type
- How Python Is Used in Finance: Real Wall Street Use Cases
- How I Learned Python in 3 Months and Got a Job: My Honest Story
- Python for Beginners: Complete 2026 Roadmap, Step-by-Step Guide
- TensorFlow vs PyTorch in 2025: Which One Should You Learn?
- How I Solve Any Programming Problem (My Personal Framework)
- Data Structures for Humans: Finally Understanding Arrays, Trees, Graphs
Discussion
0Sign in with Google to comment, reply and manage your own comments.
Frequently Asked Questions
What is the requests library in Python?
How do I handle API authentication in Python requests?
How do I handle errors with Python requests?
What is requests.Session and when should I use it?
Problem Solver and Cloud Expert
Cloud and infrastructure contributor supporting AiTechWorldsโ deployment, reliability, and platform operations.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of โThe Python Developer's Guide to APIs: Requests Library Deep Diveโ.
Related Articles
FastAPI Tutorial: Building Your First REST API in 30 Minutes
A hands-on FastAPI tutorial for beginners: build a fully functional REST API in 30 minutes with CRUD endpoints, request validation, and automatic docs.
Async Python: Why Your Programs Are Slow and How to Fix Them
A practical Python async/await tutorial: understand why synchronous code is slow for I/O tasks and how asyncio, async def, and await make programs dramatically faster.
Python for Absolute Beginners: Your First 30 Days Roadmap (2026)
A structured 30-day Python learning roadmap for 2026: daily milestones, free resources, projects, and the exact path from zero to writing real Python programs.
Web Scraping with Python: A Gentle Introduction for Beginners
A beginner-friendly Python web scraping guide using requests and BeautifulSoup: extract data from websites, handle pagination, and store results in 2026.