Of course! Here's a comprehensive guide on how to send multipart/form-data requests in Python using the popular requests library.

This is the standard format for sending data that includes files, text, and binary data in a single request, commonly used for uploading files or submitting complex web forms.
What is multipart/form-data?
It's an encoding type that allows you to send multiple pieces of data in a single HTTP request. Each piece of data is called a "part" and is separated by a unique boundary. This is essential for:
- Uploading files (images, documents, etc.)
- Sending a mix of text fields and files (e.g., a user's name and their profile picture)
- Sending large amounts of text data that might be problematic for URL encoding.
The Easiest Way: Using requests with a Dictionary
The requests library simplifies this process immensely. You can pass a dictionary of your data to the files parameter, and requests will automatically construct the multipart/form-data payload for you.
Scenario: Uploading a file and some text data
Let's say you want to upload a file named my_document.txt and also send a description field.

File to upload (my_document.txt):
Hello, this is the content of my document.
It has multiple lines.
Python Script:
import requests
# The URL of the server endpoint that accepts the form data
url = 'https://httpbin.org/post' # httpbin.org is a great testing service
# 1. Define the text fields
# This is a standard dictionary for non-file data
data = {
'description': 'A sample document for testing file uploads.'
}
# 2. Define the file(s) to upload
# The key 'file' will be the name of the form field on the server.
# The value is a tuple: (filename, file_object, content_type)
with open('my_document.txt', 'rb') as f:
files = {
'file': ('my_document.txt', f, 'text/plain')
}
# 3. Send the POST request
# The `files` parameter is what tells requests to use multipart/form-data
# The `data` parameter is for the other form fields
response = requests.post(url, data=data, files=files)
# 4. Check the response
print(f"Status Code: {response.status_code}")
print("Response JSON:")
print(response.json())
Explanation of the files Dictionary:
- Key (
'file'): This is the name of the form field as expected by the server. If your HTML form is<input name="user_avatar">, then the key must be'user_avatar'. - Value (a tuple): The tuple contains three parts:
'my_document.txt': The filename you want to send to the server. This can be different from your local file's name.f: The file object opened in binary read mode ('rb').requestswill read the content from this object.'text/plain': The MIME type of the file. This helps the server understand the file's format. Common types includeimage/jpeg,application/pdf,text/csv, etc. If you omit this,requestswill try to guess it.
More Advanced File Handling
Uploading Multiple Files
You can easily upload multiple files by adding more entries to the files dictionary.
import requests
url = 'https://httpbin.org/post'
# Multiple files in the same request
files = {
'file1': ('report.pdf', open('report.pdf', 'rb'), 'application/pdf'),
'file2': ('image.png', open('image.png', 'rb'), 'image/png')
}
response = requests.post(url, files=files)
print(response.status_code)
Uploading In-Memory Data (without a file on disk)
If you have file data already in a variable (e.g., from another source), you can use io.BytesIO to treat it as a file-like object.

import requests
import io
url = 'https://httpbin.org/post'
# File data in memory
file_content = "This is a virtual file, stored in a string."
file_bytes = file_content.encode('utf-8')
file_like_object = io.BytesIO(file_bytes)
# The 'file' parameter is the same as before
files = {
'file': ('virtual_file.txt', file_like_object, 'text/plain')
}
response = requests.post(url, files=files)
print(response.status_code)
Manual Construction (The Hard Way)
While you should almost always use the requests built-in method, understanding how the raw multipart/form-data looks can be very helpful for debugging or if you're using a different library.
The format uses a boundary string to separate parts. Here’s how you would construct it manually.
import requests
url = 'https://httpbin.org/post'
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
# The text part
text_part = f"--{boundary}\n"
text_part += 'Content-Disposition: form-data; name="description"\n\n'
text_part += 'A manually constructed form part.\n'
# The file part
with open('my_document.txt', 'rb') as f:
file_content = f.read()
file_part = f"--{boundary}\n"
file_part += 'Content-Disposition: form-data; name="file"; filename="my_document.txt"\n'
file_part += 'Content-Type: text/plain\n\n'
file_part += file_content.decode('utf-8') # Add file content
file_part += f"\n--{boundary}--" # The final boundary ends with '--'
# The full payload
payload = text_part + file_part
# Set the correct Content-Type header
headers = {
'Content-Type': f'multipart/form-data; boundary={boundary}'
}
response = requests.post(url, data=payload, headers=headers)
print(response.status_code)
As you can see, this is much more complex and error-prone. Stick with the requests built-in files parameter.
Key Differences: data vs. files
It's crucial to understand the difference between the data and files parameters in requests.post().
| Parameter | Use Case | Content-Type | Example |
|---|---|---|---|
data |
Simple key-value pairs (strings, numbers). | application/x-www-form-urlencoded (default) or multipart/form-data (if files is also present). |
requests.post(url, data={'key': 'value'}) |
files |
For uploading files or mixing files with text fields. | Forces Content-Type: multipart/form-data. |
requests.post(url, files={'file': open(...)}) |
Important: If you provide both data and files to a requests.post() call, requests will automatically combine them into a single multipart/form-data request. The data fields will be text parts, and the files fields will be file parts.
Summary and Best Practices
- Use the
requestslibrary. It's the standard for HTTP in Python. - For
multipart/form-data, use thefilesparameter. - For text fields, use the
dataparameter. - When using
files, open your file in binary read mode ('rb'). - Provide a filename and a MIME type in the
filestuple for best compatibility. - Always check the
response.status_codeto ensure your request was successful (e.g.,200or201). - Use a service like
httpbin.org/postfor testing, as it will echo back the exact request it received, allowing you to verify that your form data and files were formatted correctly.
