Skip to main content

How to Send Multipart/Form-Data Requests in Python with requests

Sending data using multipart/form-data encoding is essential for file uploads and submitting complex forms via HTTP POST requests.

This guide explores how to send multipart/form-data requests using the popular requests library in Python, covering scenarios with and without file uploads, and introducing the requests-toolbelt for more advanced control.

Sending Multipart/Form-Data with the files Parameter

The requests library automatically uses multipart/form-data encoding when you provide the files argument to requests.post().

Sending Data Without Actual Files

You can use the files parameter even if you're not uploading files. This forces the Content-Type header to multipart/form-data.

import requests
from pprint import pprint

url = 'https://httpbin.org/post'
response = requests.post(
url,
files={'id': '1', 'site': 'tutorialreference.com'}, # Use files param for multipart
timeout=30
)

print(f"Status Code: {response.status_code}")
if response.ok:
# Print headers to verify Content-Type
pprint(response.json().get('headers'))

Output:

Status Code: 200
{'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Content-Length': '...',
'Content-Type': 'multipart/form-data; boundary=...', # Correct header
'Host': 'httpbin.org', ... }
  • The files parameter can be used to send any data using multipart/form-data encoding.
note

Do not manually set the Content-Type header when using the files parameter. requests handles setting the correct Content-Type including the necessary boundary parameter.

Sending Files

To upload a file, provide a file object opened in binary read mode ('rb') within the files dictionary:

import requests

url = 'https://httpbin.org/post'
try:
# Assume 'data.json' exists in the same directory
with open('data.json', 'rb') as f:
files_to_upload = {'file': f}
response = requests.post(url, files=files_to_upload, timeout=30)

print(response.text)
print(f"Status Code: {response.status_code}")

except FileNotFoundError:
print("Error: data.json not found.")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")

Explicitly Setting Filename, Content-Type, and Headers

For more control over file uploads, you can provide a tuple as the value in the files dictionary. The tuple format allows specifying (filename, file_object, content_type, custom_headers).

import requests

url = 'https://httpbin.org/post'
try:
# Assume 'example.xlsx' exists
with open('example.xlsx', 'rb') as f:
files_spec = {
'file': (
'custom_filename.xlsx', # Custom filename
f, # File object
'application/vnd.ms-excel', # Specific Content-Type
{'Expires': '0'} # Custom headers
)
}
response = requests.post(url, files=files_spec, timeout=30)

print(response.text)
print(f"Status Code: {response.status_code}")

except FileNotFoundError:
print("Error: example.xlsx not found.")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
  • Plain Text Fields: To send regular form fields alongside files using the files parameter structure, set the filename part of the tuple to None:
    files_with_text = {
    'session_id': (None, 'some_session_value'), # Plain text field
    'upload': ('report.txt', open('report.txt', 'rb')) # File field
    }
    # response = requests.post(url, files=files_with_text)
  • String as File Content: You can also provide a string directly as the file content:
    files_string_content = {
    'file': (
    'example.csv',
    'some,data,to,send\nanother,row,to,send\n' # String content
    )
    }
    # response = requests.post(url, files=files_string_content)

Sending Multipart/Form-Data with requests-toolbelt

The requests-toolbelt library offers the MultipartEncoder for more complex scenarios and streaming uploads.

Installing requests-toolbelt

pip install requests-toolbelt
# or with pip3
pip3 install requests-toolbelt

Using MultipartEncoder

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

url = 'https://httpbin.org/post'

try:
# Assume 'example.xlsx' exists
with open('example.xlsx', 'rb') as f:
multipart_data = MultipartEncoder(
fields={
# Plain text fields
'field1': 'value1',
'field2': 'value2',
# File upload field (filename, file_object, content_type)
'file': ('custom_name.xlsx', f, 'application/vnd.ms-excel')
}
)

response = requests.post(
url,
data=multipart_data, # Pass encoder to 'data'
headers={'Content-Type': multipart_data.content_type}, # Set header from encoder
timeout=30
)

print(response.text)
print(f"Status Code: {response.status_code}")

except FileNotFoundError:
print("Error: example.xlsx not found.")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
  • Create a MultipartEncoder instance, defining fields with their names, values, and optional content types.
  • Pass the MultipartEncoder object to the data parameter of requests.post().
  • Crucially, set the Content-Type header using multipart_data.content_type.

Conclusion

This guide demonstrated how to send multipart/form-data requests using Python's requests library.

You learned the primary method using the files parameter for both simple data and file uploads, how to customize file details using tuples, and how to use the requests-toolbelt library for more advanced multipart encoding. Choosing the right method depends on the complexity of the data and files you need to send.