The Requests library is the de facto standard for making HTTP requests in Python. It wraps the complexities of urllib into a clean, intuitive API that reads almost like English. Whether you’re calling APIs, scraping pages, or automating web interactions, Requests is usually the right tool.
Installation
pip install requests
Making a GET Request
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
print(response.status_code)
# Output: 200
print(response.json())
# Output: {'userId': 1, 'id': 1, 'title': '...', 'body': '...'}
Use .json() to parse JSON responses directly β no need to import json and call json.loads() separately.
Query Parameters
Pass parameters as a dictionary instead of manually building URL strings:
params = {"userId": 1, "completed": False}
response = requests.get(
"https://jsonplaceholder.typicode.com/todos",
params=params
)
print(response.url)
# Output: https://jsonplaceholder.typicode.com/todos?userId=1&completed=False
Custom Headers
headers = {
"Authorization": "Bearer YOUR_TOKEN",
"Accept": "application/json"
}
response = requests.get("https://api.example.com/data", headers=headers)
Making a POST Request
Use json= for JSON payloads (sets Content-Type automatically) or data= for form-encoded data:
# JSON payload
payload = {"title": "foo", "body": "bar", "userId": 1}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=payload
)
print(response.status_code)
# Output: 201
print(response.json())
# Output: {'title': 'foo', 'body': 'bar', 'userId': 1, 'id': 101}
# Form-encoded data
data = {"username": "carl", "password": "secret"}
response = requests.post("https://example.com/login", data=data)
Handling Errors
Check status codes explicitly, or use raise_for_status() to throw exceptions on 4xx/5xx responses:
response = requests.get("https://jsonplaceholder.typicode.com/nonexistent")
# Manual check
if response.status_code == 404:
print("Not found")
# Or raise an exception automatically
try:
response.raise_for_status()
except requests.HTTPError as e:
print(f"HTTP error: {e}")
Timeouts
Always set a timeout in production code. Without one, your program can hang indefinitely if the server doesn’t respond:
try:
response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
print("Request timed out after 5 seconds")
except requests.ConnectionError:
print("Failed to connect")
The timeout parameter accepts seconds as an integer or float. You can also pass a tuple (connect_timeout, read_timeout) for more control.
Sessions
Sessions persist settings (cookies, headers, auth) across multiple requests and reuse the underlying TCP connection for better performance:
with requests.Session() as session:
session.headers.update({"Authorization": "Bearer TOKEN"})
# Both requests use the same auth header and TCP connection
users = session.get("https://api.example.com/users")
posts = session.get("https://api.example.com/posts")
Sessions are also context managers β the connection pool is cleaned up when the with block exits.
Response Object
The response object has several useful attributes:
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
response.status_code # 200
response.headers # dict-like object of response headers
response.text # response body as string
response.content # response body as bytes
response.json() # parsed JSON
response.url # final URL (after redirects)
response.elapsed # time delta for the request
response.encoding # detected encoding
Security Best Practices (Updated 2026)
Critical security considerations when using Requests:
π Validate All URLs
from urllib.parse import urlparse
import ipaddress
def is_safe_url(url):
"""Validate URL to prevent SSRF attacks"""
try:
parsed = urlparse(url)
# Only allow HTTPS for sensitive operations
if parsed.scheme not in ['https']:
return False
# Block private/internal IPs
try:
ip = ipaddress.ip_address(parsed.hostname)
if ip.is_private or ip.is_loopback:
return False
except (ValueError, TypeError):
pass # Hostname, not IP
return True
except Exception:
return False
# Safe usage
user_url = "https://api.example.com/data"
if is_safe_url(user_url):
response = requests.get(user_url)
π‘οΈ Set Timeouts Always
# Prevent hanging requests
response = requests.get(url, timeout=(5, 30)) # (connect, read)
π Use Sessions for Authentication
session = requests.Session()
session.headers.update({
'Authorization': 'Bearer your-token',
'User-Agent': 'YourApp/1.0'
})
# Certificate verification (default, but explicit is better)
session.verify = True
β οΈ Common Security Pitfalls
- Never disable SSL verification in production (
verify=False) - Always validate URLs from user input to prevent SSRF
- Use environment variables for API keys, not hardcoded strings
- Set reasonable timeouts to prevent DoS
Related Security Topics
π‘οΈ Python Security
- Python SSRF Prevention Guide - Comprehensive SSRF protection for Python applications
- Python Security Best Practices - Secure coding patterns and vulnerability prevention
- SSRF Defense Guide - Server-side request forgery prevention strategies
π Python Development
- The 5 Coolest Things About Using Python - Why Python’s ecosystem makes libraries like Requests possible
- Context Managers in Python - Sessions work well with
withstatements for clean resource handling - Python 3.13 Major Step Forward - Latest Python features and security improvements