Introduction
Python developers often need to work with HTTP requests and sometimes need to integrate cURL commands
into their applications. While Python's requests library is typically the preferred choice
for HTTP operations, there are scenarios where executing cURL commands directly can be beneficial.
This comprehensive guide covers multiple approaches to using cURL in Python, from converting cURL commands to Python requests to executing cURL directly using subprocess.
Methods Overview
There are three main approaches to handle cURL in Python:
- Convert to requests: Convert cURL commands to Python requests code (recommended)
- Execute via subprocess: Run cURL commands directly using subprocess module
- Use curlify: Convert requests back to cURL for debugging
Method 1: Using Python Requests Library
Basic GET Request
Converting a simple GET cURL command:
# cURL command
curl 'https://api.example.com/users'
# Python requests equivalent
import requests
response = requests.get('https://api.example.com/users')
print(response.json())
GET Request with Headers
# cURL with headers
curl 'https://api.example.com/data' \
-H 'Authorization: Bearer token123' \
-H 'Accept: application/json'
# Python requests equivalent
import requests
headers = {
'Authorization': 'Bearer token123',
'Accept': 'application/json'
}
response = requests.get('https://api.example.com/data', headers=headers)
data = response.json()
POST Request with JSON Data
# cURL POST with JSON
curl -X POST 'https://api.example.com/users' \
-H 'Content-Type: application/json' \
-d '{"name": "John", "email": "[email protected]"}'
# Python requests equivalent
import requests
import json
url = 'https://api.example.com/users'
data = {
'name': 'John',
'email': '[email protected]'
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, json=data, headers=headers)
# Or alternatively:
# response = requests.post(url, data=json.dumps(data), headers=headers)
POST Request with Form Data
# cURL with form data
curl -X POST 'https://api.example.com/submit' \
-d 'username=admin' \
-d 'password=secret'
# Python requests equivalent
import requests
url = 'https://api.example.com/submit'
data = {
'username': 'admin',
'password': 'secret'
}
response = requests.post(url, data=data)
File Upload
# cURL file upload curl -X POST 'https://api.example.com/upload' \ -F '[email protected]' \ -F 'description=Important document' # Python requests equivalent import requests url = 'https://api.example.com/upload' files = {'file': open('document.pdf', 'rb')} data = {'description': 'Important document'} response = requests.post(url, files=files, data=data) # Don't forget to close the file files['file'].close() # Or use context manager (recommended) with open('document.pdf', 'rb') as f: files = {'file': f} response = requests.post(url, files=files, data=data)
Method 2: Using Subprocess Module
Basic Subprocess Execution
Sometimes you need to execute cURL commands directly:
import subprocess
import json
def execute_curl(curl_command):
"""Execute a cURL command and return the response"""
try:
result = subprocess.run(
curl_command,
shell=True,
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
return result.stdout
else:
print(f"Error: {result.stderr}")
return None
except subprocess.TimeoutExpired:
print("cURL command timed out")
return None
# Example usage
curl_cmd = "curl -s 'https://api.example.com/users'"
response = execute_curl(curl_cmd)
if response:
data = json.loads(response)
print(data)
Advanced Subprocess with Error Handling
import subprocess
import json
import shlex
class CurlExecutor:
def __init__(self, timeout=30):
self.timeout = timeout
def execute(self, curl_command):
"""
Execute cURL command safely with proper error handling
"""
try:
# Use shlex.split for safer command parsing
if isinstance(curl_command, str):
cmd_args = shlex.split(curl_command)
else:
cmd_args = curl_command
result = subprocess.run(
cmd_args,
capture_output=True,
text=True,
timeout=self.timeout,
check=False
)
return {
'stdout': result.stdout,
'stderr': result.stderr,
'returncode': result.returncode,
'success': result.returncode == 0
}
except subprocess.TimeoutExpired:
return {
'stdout': '',
'stderr': 'Command timed out',
'returncode': -1,
'success': False
}
except Exception as e:
return {
'stdout': '',
'stderr': str(e),
'returncode': -1,
'success': False
}
# Usage example
executor = CurlExecutor(timeout=60)
result = executor.execute([
'curl', '-s', '-H', 'Accept: application/json',
'https://api.example.com/data'
])
if result['success']:
data = json.loads(result['stdout'])
print(data)
else:
print(f"Error: {result['stderr']}")
Method 3: Using curlify for Debugging
Installation and Basic Usage
# Install curlify
pip install curlify
# Convert requests to cURL for debugging
import requests
from curlify import to_curl
# Make a request
response = requests.get(
'https://api.example.com/users',
headers={'Authorization': 'Bearer token123'}
)
# Convert to cURL command
curl_command = to_curl(response.request)
print(curl_command)
# Output:
# curl -X GET -H 'Authorization: Bearer token123' https://api.example.com/users
Practical Examples
API Testing Script
import requests
import json
class APITester:
def __init__(self, base_url, auth_token=None):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
if auth_token:
self.session.headers.update({
'Authorization': f'Bearer {auth_token}'
})
def test_endpoint(self, endpoint, method='GET', data=None, expected_status=200):
"""Test an API endpoint"""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
try:
if method.upper() == 'GET':
response = self.session.get(url)
elif method.upper() == 'POST':
response = self.session.post(url, json=data)
elif method.upper() == 'PUT':
response = self.session.put(url, json=data)
elif method.upper() == 'DELETE':
response = self.session.delete(url)
else:
raise ValueError(f"Unsupported method: {method}")
success = response.status_code == expected_status
return {
'success': success,
'status_code': response.status_code,
'response': response.json() if response.content else None,
'headers': dict(response.headers)
}
except requests.exceptions.RequestException as e:
return {
'success': False,
'error': str(e)
}
# Usage
tester = APITester('https://api.example.com', auth_token='your-token')
# Test GET endpoint
result = tester.test_endpoint('/users')
print(f"GET /users: {'✓' if result['success'] else '✗'}")
# Test POST endpoint
new_user = {'name': 'John', 'email': '[email protected]'}
result = tester.test_endpoint('/users', method='POST', data=new_user, expected_status=201)
print(f"POST /users: {'✓' if result['success'] else '✗'}")
Converting Complex cURL to Python
# Complex cURL command
curl_command = """
curl -X POST 'https://api.example.com/complex' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer token123' \
-H 'X-Custom-Header: value' \
-d '{
"user": {
"name": "John Doe",
"email": "[email protected]",
"preferences": {
"theme": "dark",
"language": "en"
}
}
}'
"""
# Python equivalent
import requests
def make_complex_request():
url = 'https://api.example.com/complex'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'X-Custom-Header': 'value'
}
data = {
'user': {
'name': 'John Doe',
'email': '[email protected]',
'preferences': {
'theme': 'dark',
'language': 'en'
}
}
}
try:
response = requests.post(url, json=data, headers=headers, timeout=30)
response.raise_for_status() # Raise an exception for bad status codes
return {
'success': True,
'data': response.json(),
'status_code': response.status_code
}
except requests.exceptions.Timeout:
return {'success': False, 'error': 'Request timed out'}
except requests.exceptions.RequestException as e:
return {'success': False, 'error': str(e)}
# Execute the request
result = make_complex_request()
if result['success']:
print("Success:", result['data'])
else:
print("Error:", result['error'])
Best Practices
Error Handling
Always implement proper error handling:
- Use try-catch blocks for network requests
- Set appropriate timeouts
- Check response status codes
- Handle different types of exceptions
Security Considerations
Security best practices:
- Never hardcode sensitive data like API keys in your code
- Use environment variables for configuration
- Validate all input data
- Use HTTPS for all API communications
- Be careful with subprocess shell=True
Performance Optimization
# Use session for multiple requests
import requests
# Create a session to reuse connections
session = requests.Session()
session.headers.update({'User-Agent': 'MyApp/1.0'})
# Make multiple requests efficiently
for i in range(10):
response = session.get(f'https://api.example.com/item/{i}')
print(response.json())
# Close the session when done
session.close()
# Or use context manager
with requests.Session() as session:
session.headers.update({'User-Agent': 'MyApp/1.0'})
for i in range(10):
response = session.get(f'https://api.example.com/item/{i}')
print(response.json())
Common Issues and Solutions
SSL Certificate Issues
# Skip SSL verification (not recommended for production)
response = requests.get('https://example.com', verify=False)
# Use custom certificate bundle
response = requests.get('https://example.com', verify='/path/to/certificate.pem')
# Handle SSL errors gracefully
try:
response = requests.get('https://example.com')
except requests.exceptions.SSLError as e:
print(f"SSL Error: {e}")
# Fallback to HTTP or handle appropriately
Timeout and Connection Issues
# Set different timeouts
response = requests.get(
'https://api.example.com/data',
timeout=(5, 30) # (connection timeout, read timeout)
)
# Retry mechanism
import time
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_session_with_retries():
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
# Usage
session = create_session_with_retries()
response = session.get('https://api.example.com/data', timeout=30)
Conclusion
While cURL is a powerful command-line tool, Python's requests library provides a more
Pythonic and feature-rich approach to HTTP operations. Use the requests library for most
scenarios, and reserve subprocess execution of cURL for specific cases where you need to integrate
with existing scripts or when specific cURL features are required.
Remember to always implement proper error handling, use secure practices, and optimize for performance when building production applications.