Requests
Most popular HTTP library for Python with 'HTTP for Humans' concept providing simple and intuitive API. Comprehensive features including session management, SSL verification, automatic redirects, authentication, cookie handling, and file uploads.
Library
Requests
Overview
Requests is "a simple and user-friendly HTTP library for Python" developed as the most popular HTTP client library in the Python ecosystem. With the concept of "HTTP for Humans," it provides complex HTTP processing through a simple and intuitive API. Comprehensively supporting features necessary for web API integration such as authentication, session management, SSL certificate verification, and automatic JSON/XML parsing, it has established itself as the de facto standard library for Python developers.
Details
Requests 2025 edition continues to maintain its solid position as the definitive Python HTTP communication solution. With over 15 years of development experience, it boasts mature APIs and excellent stability, being widely adopted in major web framework environments such as Django, Flask, and FastAPI. Designed with emphasis on simple and readable code writing, it enables HTTP communication implementation in a Pythonic and natural way. It provides rich features that meet enterprise-level HTTP communication requirements including session management, authentication systems, proxy support, SSL/TLS configuration, and streaming processing.
Key Features
- Simple API: Intuitive and readable HTTP request description
- Comprehensive Authentication Support: Support for Basic, Digest, OAuth, and custom authentication
- Session Management: Cookie persistence and efficient connection pooling
- Automatic Content Parsing: Automatic parsing for JSON, XML, HTML, etc.
- Complete SSL/TLS Support: Certificate verification and client certificate support
- Streaming Processing: Efficient processing of large files
Pros and Cons
Pros
- Overwhelming adoption rate in Python ecosystem with abundant learning resources
- High development efficiency and code readability through simple and intuitive API
- Enterprise-level support through comprehensive authentication and session management
- Rich community support and third-party extension features
- Excellent integration with web frameworks like Django and Flask
- Stable long-term support and backward compatibility maintenance
Cons
- Synchronous processing-based, unsuitable for CPU-bound tasks
- Requires dedicated libraries like aiohttp for asynchronous processing
- Limited HTTP/2 support (requires requests-http2)
- Performance constraints with large numbers of parallel requests
- Relatively large bundle size due to many dependency libraries
- Migration challenges due to end of support for older Python versions
Reference Pages
Code Examples
Installation and Basic Setup
# Install Requests
pip install requests
# Security-enhanced version (recommended)
pip install requests[security]
# Complete installation including dependencies
pip install requests[socks]
# Verification in Python environment
python -c "import requests; print(requests.__version__)"
Basic Requests (GET/POST/PUT/DELETE)
import requests
# Basic GET request
response = requests.get('https://api.example.com/users')
print(response.status_code) # 200
print(response.headers['content-type']) # application/json
print(response.text) # Response text
data = response.json() # Parse as JSON
print(data)
# GET request with query parameters
params = {'page': 1, 'limit': 10, 'sort': 'created_at'}
response = requests.get('https://api.example.com/users', params=params)
print(response.url) # https://api.example.com/users?page=1&limit=10&sort=created_at
# POST request (sending JSON)
user_data = {
'name': 'John Doe',
'email': '[email protected]',
'age': 30
}
response = requests.post(
'https://api.example.com/users',
json=user_data, # Automatically sets Content-Type: application/json
headers={'Authorization': 'Bearer your-token'}
)
if response.status_code == 201:
created_user = response.json()
print(f"User created: ID={created_user['id']}")
else:
print(f"Error: {response.status_code} - {response.text}")
# POST request (sending form data)
form_data = {'username': 'testuser', 'password': 'secret123'}
response = requests.post('https://api.example.com/login', data=form_data)
# PUT request (data update)
updated_data = {'name': 'Jane Doe', 'email': '[email protected]'}
response = requests.put(
'https://api.example.com/users/123',
json=updated_data,
headers={'Authorization': 'Bearer your-token'}
)
# DELETE request
response = requests.delete(
'https://api.example.com/users/123',
headers={'Authorization': 'Bearer your-token'}
)
if response.status_code == 204:
print("User deleted successfully")
# Detailed response attribute inspection
print(f"Status code: {response.status_code}")
print(f"Reason: {response.reason}")
print(f"Headers: {response.headers}")
print(f"Encoding: {response.encoding}")
print(f"Request URL: {response.url}")
print(f"History: {response.history}") # Redirect history
Advanced Configuration and Customization (Headers, Authentication, Timeout, etc.)
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
# Custom header configuration
headers = {
'User-Agent': 'MyApp/1.0 (Python Requests)',
'Accept': 'application/json',
'Accept-Language': 'en-US,ja-JP',
'X-API-Version': 'v2',
'X-Request-ID': 'req-12345'
}
response = requests.get('https://api.example.com/data', headers=headers)
# Basic authentication
response = requests.get(
'https://api.example.com/private',
auth=('username', 'password')
)
# Or explicitly use HTTPBasicAuth
auth = HTTPBasicAuth('username', 'password')
response = requests.get('https://api.example.com/private', auth=auth)
# Digest authentication
digest_auth = HTTPDigestAuth('username', 'password')
response = requests.get('https://api.example.com/digest', auth=digest_auth)
# Bearer Token authentication
headers = {'Authorization': 'Bearer your-jwt-token'}
response = requests.get('https://api.example.com/protected', headers=headers)
# Timeout configuration
try:
# Connection timeout 5 seconds, read timeout 10 seconds
response = requests.get('https://api.example.com/slow', timeout=(5, 10))
# Overall timeout of 15 seconds
response = requests.get('https://api.example.com/data', timeout=15)
except requests.exceptions.Timeout:
print("Request timed out")
# SSL configuration and client certificates
response = requests.get(
'https://secure-api.example.com/data',
cert=('/path/to/client.cert', '/path/to/client.key'), # Client certificate
verify='/path/to/ca-bundle.crt' # CA certificate bundle
)
# Disable SSL certificate verification (development only)
response = requests.get('https://self-signed.example.com/', verify=False)
# Proxy configuration
proxies = {
'http': 'http://proxy.example.com:8080',
'https': 'http://proxy.example.com:8080'
}
response = requests.get('https://api.example.com/data', proxies=proxies)
# Authenticated proxy
proxies = {
'http': 'http://user:[email protected]:8080',
'https': 'http://user:[email protected]:8080'
}
# Cookie configuration
cookies = {'session_id': 'abc123', 'user_pref': 'dark_mode'}
response = requests.get('https://api.example.com/user-data', cookies=cookies)
# Redirect control
response = requests.get(
'https://api.example.com/redirect',
allow_redirects=False, # Disable redirects
max_redirects=5 # Maximum number of redirects
)
Error Handling and Retry Functionality
import requests
from requests.exceptions import (
ConnectionError, Timeout, RequestException,
HTTPError, TooManyRedirects
)
import time
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
# Comprehensive error handling
def safe_request(url, **kwargs):
try:
response = requests.get(url, **kwargs)
# HTTP status code check
response.raise_for_status() # Raises HTTPError for 4xx/5xx
return response
except ConnectionError as e:
print(f"Connection error: {e}")
print("Please check your network connection")
except Timeout as e:
print(f"Timeout error: {e}")
print("Request timed out")
except HTTPError as e:
print(f"HTTP error: {e}")
print(f"Status code: {response.status_code}")
print(f"Response: {response.text}")
except TooManyRedirects as e:
print(f"Redirect error: {e}")
print("Too many redirects")
except RequestException as e:
print(f"Request error: {e}")
print("Unexpected error occurred")
return None
# Usage example
response = safe_request('https://api.example.com/data', timeout=10)
if response:
data = response.json()
print(data)
# Manual retry implementation
def request_with_retry(url, max_retries=3, backoff_factor=1, **kwargs):
for attempt in range(max_retries + 1):
try:
response = requests.get(url, **kwargs)
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
if attempt == max_retries:
print(f"Maximum attempts reached: {e}")
raise
wait_time = backoff_factor * (2 ** attempt)
print(f"Attempt {attempt + 1} failed. Retrying in {wait_time} seconds...")
time.sleep(wait_time)
# Usage example
try:
response = request_with_retry(
'https://api.example.com/unstable',
max_retries=3,
backoff_factor=1,
timeout=10
)
print("Request successful:", response.status_code)
except requests.exceptions.RequestException as e:
print("Finally failed:", e)
# Advanced retry configuration using urllib3's Retry class
retry_strategy = Retry(
total=5, # Total number of attempts
status_forcelist=[429, 500, 502, 503, 504], # Status codes to retry
method_whitelist=["HEAD", "GET", "OPTIONS"], # Methods to retry
backoff_factor=1, # Backoff factor
respect_retry_after_header=True # Respect Retry-After header
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
# Request using session
response = session.get('https://api.example.com/data', timeout=10)
# Status code-specific handling
response = requests.get('https://api.example.com/status-check')
if response.status_code == 200:
print("Success: ", response.json())
elif response.status_code == 401:
print("Authentication error: Please check your token")
elif response.status_code == 403:
print("Permission error: Access denied")
elif response.status_code == 404:
print("Not found: Resource does not exist")
elif response.status_code == 429:
print("Rate limit: Please wait before retrying")
elif response.status_code >= 500:
print("Server error: Problem on server side")
else:
print(f"Unexpected status: {response.status_code}")
Concurrent Processing and Asynchronous Requests
import requests
import concurrent.futures
from threading import Thread
import queue
import time
# Parallel fetching of multiple URLs
def fetch_url(url):
try:
response = requests.get(url, timeout=10)
return {
'url': url,
'status_code': response.status_code,
'content_length': len(response.content),
'success': True
}
except requests.exceptions.RequestException as e:
return {
'url': url,
'error': str(e),
'success': False
}
# Parallel processing using ThreadPoolExecutor
urls = [
'https://api.example.com/users',
'https://api.example.com/posts',
'https://api.example.com/comments',
'https://api.example.com/categories'
]
def parallel_fetch_threadpool(urls, max_workers=5):
results = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
# Submit all URLs for parallel execution
future_to_url = {executor.submit(fetch_url, url): url for url in urls}
for future in concurrent.futures.as_completed(future_to_url):
result = future.result()
results.append(result)
if result['success']:
print(f"Success: {result['url']} - {result['status_code']}")
else:
print(f"Failed: {result['url']} - {result['error']}")
return results
# Usage example
results = parallel_fetch_threadpool(urls)
successful_results = [r for r in results if r['success']]
print(f"Success: {len(successful_results)}/{len(urls)}")
# Efficient parallel processing with sessions
def fetch_with_session(session, url):
try:
response = session.get(url, timeout=10)
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error {url}: {e}")
return None
def parallel_fetch_with_session(urls, max_workers=5):
results = []
# Sessions share connection pools for efficiency
session = requests.Session()
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(fetch_with_session, session, url) for url in urls]
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
results.append(result)
session.close()
return results
# Pagination-aware progressive data fetching
def fetch_all_pages(base_url, headers=None, max_pages=None):
all_data = []
page = 1
session = requests.Session()
if headers:
session.headers.update(headers)
while True:
try:
params = {'page': page, 'per_page': 100}
response = session.get(base_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if not data or (isinstance(data, list) and len(data) == 0):
break # End if no data
if isinstance(data, dict) and 'items' in data:
items = data['items']
all_data.extend(items)
# End if no next page
if not data.get('has_more', True) or len(items) == 0:
break
else:
all_data.extend(data)
print(f"Page {page} completed: {len(data if isinstance(data, list) else data.get('items', []))} items")
page += 1
# Check maximum page limit
if max_pages and page > max_pages:
break
# Wait to reduce API load
time.sleep(0.1)
except requests.exceptions.RequestException as e:
print(f"Error on page {page}: {e}")
break
session.close()
print(f"Total items fetched: {len(all_data)}")
return all_data
# Usage example
headers = {'Authorization': 'Bearer your-token'}
all_posts = fetch_all_pages('https://api.example.com/posts', headers=headers)
# Duplicate request prevention and caching
class RequestCache:
def __init__(self):
self.cache = {}
self.pending = {}
def get_or_fetch(self, url, **kwargs):
if url in self.cache:
print(f"Cache hit: {url}")
return self.cache[url]
if url in self.pending:
print(f"Duplicate request in progress: {url}")
return None
try:
self.pending[url] = True
response = requests.get(url, **kwargs)
response.raise_for_status()
result = response.json()
self.cache[url] = result
return result
finally:
self.pending.pop(url, None)
# Cache usage example
cache = RequestCache()
data1 = cache.get_or_fetch('https://api.example.com/users/123')
data2 = cache.get_or_fetch('https://api.example.com/users/123') # Retrieved from cache
Framework Integration and Practical Examples
import requests
from requests.auth import AuthBase
import json
import os
from datetime import datetime, timedelta
# API client for Django/Flask integration
class APIClient:
def __init__(self, base_url, token=None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'MyApp/1.0'
})
if token:
self.session.headers['Authorization'] = f'Bearer {token}'
def get(self, endpoint, **kwargs):
url = f"{self.base_url}/{endpoint.lstrip('/')}"
return self._make_request('GET', url, **kwargs)
def post(self, endpoint, data=None, **kwargs):
url = f"{self.base_url}/{endpoint.lstrip('/')}"
return self._make_request('POST', url, json=data, **kwargs)
def put(self, endpoint, data=None, **kwargs):
url = f"{self.base_url}/{endpoint.lstrip('/')}"
return self._make_request('PUT', url, json=data, **kwargs)
def delete(self, endpoint, **kwargs):
url = f"{self.base_url}/{endpoint.lstrip('/')}"
return self._make_request('DELETE', url, **kwargs)
def _make_request(self, method, url, **kwargs):
try:
response = self.session.request(method, url, timeout=30, **kwargs)
response.raise_for_status()
if response.content:
return response.json()
return None
except requests.exceptions.RequestException as e:
print(f"API Error: {method} {url} - {e}")
raise
def close(self):
self.session.close()
# Usage example
client = APIClient('https://api.example.com/v1', token='your-jwt-token')
try:
# Get user list
users = client.get('users', params={'page': 1, 'limit': 50})
# Create new user
new_user = client.post('users', data={
'name': 'John Doe',
'email': '[email protected]'
})
# Update user
updated_user = client.put(f'users/{new_user["id"]}', data={
'name': 'Jane Doe'
})
finally:
client.close()
# Auto-refresh authentication client
class AuthRefreshClient:
def __init__(self, base_url, client_id, client_secret):
self.base_url = base_url
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.token_expires_at = None
self.session = requests.Session()
def _ensure_valid_token(self):
if (not self.access_token or
not self.token_expires_at or
datetime.now() >= self.token_expires_at):
self._refresh_token()
def _refresh_token(self):
auth_data = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': 'client_credentials'
}
response = requests.post(
f"{self.base_url}/oauth/token",
data=auth_data,
timeout=10
)
response.raise_for_status()
token_data = response.json()
self.access_token = token_data['access_token']
expires_in = token_data.get('expires_in', 3600)
self.token_expires_at = datetime.now() + timedelta(seconds=expires_in)
self.session.headers['Authorization'] = f'Bearer {self.access_token}'
def request(self, method, endpoint, **kwargs):
self._ensure_valid_token()
url = f"{self.base_url}/{endpoint.lstrip('/')}"
return self.session.request(method, url, **kwargs)
# Custom authentication class
class APIKeyAuth(AuthBase):
def __init__(self, api_key, header_name='X-API-Key'):
self.api_key = api_key
self.header_name = header_name
def __call__(self, r):
r.headers[self.header_name] = self.api_key
return r
# Usage example
api_auth = APIKeyAuth('your-api-key-here')
response = requests.get('https://api.example.com/data', auth=api_auth)
# File upload
def upload_file(file_path, upload_url, additional_fields=None):
"""Upload file in multipart format"""
files = {'file': open(file_path, 'rb')}
data = additional_fields or {}
try:
response = requests.post(
upload_url,
files=files,
data=data,
headers={'Authorization': 'Bearer your-token'},
timeout=300 # 5-minute timeout
)
response.raise_for_status()
return response.json()
finally:
files['file'].close()
# Usage example
upload_result = upload_file(
'/path/to/document.pdf',
'https://api.example.com/upload',
additional_fields={'category': 'documents', 'public': 'false'}
)
# Streaming download
def download_large_file(url, local_filename, chunk_size=8192):
"""Download large file using streaming"""
with requests.get(url, stream=True, timeout=30) as response:
response.raise_for_status()
# Get file size (if available)
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(local_filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=chunk_size):
if chunk: # Filter
f.write(chunk)
downloaded += len(chunk)
# Progress display
if total_size > 0:
progress = (downloaded / total_size) * 100
print(f"Download progress: {progress:.1f}%", end='\r')
print(f"\nDownload completed: {local_filename}")
# Usage example
download_large_file(
'https://api.example.com/files/large-dataset.zip',
'/tmp/dataset.zip'
)
# Webhook receiving server (Flask integration example)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
"""Handle webhook reception from external APIs"""
# Sender verification
expected_token = os.environ.get('WEBHOOK_SECRET')
received_token = request.headers.get('X-Webhook-Token')
if not received_token or received_token != expected_token:
return jsonify({'error': 'Unauthorized'}), 401
# Process webhook data
webhook_data = request.json
# Send notification to external API
try:
notification_response = requests.post(
'https://internal-api.example.com/notifications',
json={
'event': webhook_data.get('event'),
'timestamp': datetime.now().isoformat(),
'data': webhook_data
},
headers={'Authorization': 'Bearer internal-token'},
timeout=10
)
notification_response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Notification send error: {e}")
return jsonify({'error': 'Notification failed'}), 500
return jsonify({'status': 'processed'}), 200