Python Requests: The Ultimate Guide (GET, POST, File Uploads)
How to send HTTP requests, interact with APIs, and handle files securely using Python's most popular library.
Introduction
The requests library is the de facto standard for making HTTP requests in Python. It abstracts the complexities of making requests behind a simple API so that you can focus on interacting with services and consuming data.
Whether you need to fetch a webpage, post JSON to an API, or upload a file, requests makes it intuitive.
Step 0: Set up a virtual environment
Before installing any packages, it is highly recommended to create a virtual environment. This isolates your project dependencies from the system Python, preventing version conflicts.
Check out our quick guide on how to use Python virtual environments (venv) to get set up in seconds.
Step 1: Installation
requests is not included in Python's standard library, so you must install it via pip.
Install the library:
(env) xinit@localhost:~$ pip install requests
Step 2: GET requests (and query parameters)
The most common method is GET. You can pass query parameters (the part after the ? in a URL) as a Python dictionary using the params argument. The library will automatically encode them for you.
Fetch data with parameters:
import requests
# This sends a request to: https://example.com/get?key=value&page=2
params = {'key': 'value', 'page': 2}
r = requests.get(url='https://example.com/get', params=params)
print(r.status_code)
print(r.json())
Step 3: POST requests (sending JSON)
When interacting with modern APIs, you often need to send data. Using the json parameter automatically adds the correct Content-Type: application/json header and formats your dictionary.
Send JSON data:
import requests
data = {'username': 'new_user', 'active': True}
r = requests.post(url='https://example.com/post', json=data)
print(r.status_code)
print(r.json())
Step 4: Custom headers & authentication
APIs often require authentication tokens or specific User-Agent strings. You can pass these in a dictionary to the headers argument.
Send a request with an Authorization header:
import requests
headers = {'Authorization': 'Bearer YOUR_SECRET_TOKEN'}
r = requests.get(url='https://example.com/auth', headers=headers)
print(r.status_code)
print(r.json())
Step 5: Uploading files (multipart upload)
To upload files (like submitting a form with a file input), use the files argument. Always open files in binary mode ('rb').
Upload a PDF file:
import requests
files = {'file': open('/home/user/Downloads/file.pdf', 'rb')}
r = requests.post(url='https://example.com/upload', files=files)
print(r.status_code)
print(r.json())
Step 6: Downloading files
If the response is a binary file (like an image or PDF), you should access r.content and write it to a file in binary mode ('wb').
Download and save a file:
import requests
r = requests.get(url='https://example.com/file')
with open("downloaded_file.pdf", 'wb') as f:
f.write(r.content)
Step 7: Best practices (timeouts & error handling)
In production code, always set a timeout. By default, requests will hang indefinitely if the server does not respond, which can freeze your application. Also, checking status_code manually is tedious. Use .raise_for_status() to automatically raise an exception if the request failed (e.g., 404 Not Found or 500 Server Error).
Robust request pattern:
import requests
try:
# Wait 5 seconds for a response, otherwise raise an error
r = requests.get('https://example.com/api', timeout=5)
# Raise an exception for 4xx or 5xx status codes
r.raise_for_status()
print(r.json())
except requests.exceptions.RequestException as e:
print(f'An error occurred: {e}')
Conclusion
You now have a solid foundation for working with HTTP in Python. You can fetch data, send JSON, handle authentication, and robustly manage errors and timeouts. These patterns cover the vast majority of API integration tasks.