Uploading Files and Multipart Form Data

🏷️ APIs and HTTP Requests / Sending Data in Requests

When you need to send filesβ€”like logs, configuration backups, or imagesβ€”to a server, standard JSON or form data won't work. Files require a special encoding format called multipart form data. This is how browsers upload files from HTML forms, and it's also how your Python scripts can send files to APIs.


βš™οΈ What Is Multipart Form Data?

Multipart form data is a way to send binary data (files) alongside regular text fields in a single HTTP request. Each piece of data is separated by a boundary marker, allowing the server to distinguish between different parts of the request.

Key characteristics: - Supports both text fields and file uploads in one request - Each part can have its own content type (e.g., image/png, text/csv) - The boundary string is automatically generated and included in the request headers - Required for any API endpoint that accepts file uploads


πŸ› οΈ How to Upload a Single File

To upload a file, you need to open it in binary mode and pass it as part of a dictionary to the files parameter. The server receives the file along with its filename and content type.

Basic file upload pattern: - Open the file using open('filename', 'rb') to read it in binary mode - Create a dictionary with the field name as the key and the file object as the value - Pass this dictionary to the files parameter of your request function - The library handles all the multipart encoding automatically

Example structure for a single file upload: - Import the requests library - Open the file in binary read mode - Store the file object in a dictionary: {'file': open('backup.log', 'rb')} - Send a POST request with files=file_dict - Close the file after the request completes


πŸ“Š Uploading Multiple Files

When you need to send several files at once, you can include multiple entries in the files dictionary. Each file gets its own field name, and the server processes them individually.

Multiple file upload pattern: - Open each file separately in binary mode - Create a dictionary with multiple keys, one per file - Example: {'log_file': open('error.log', 'rb'), 'config_file': open('settings.ini', 'rb')} - Send the request with the entire dictionary as the files parameter - The server receives each file under its respective field name

Alternative approach for lists of files: - Use a list of tuples instead of a dictionary - Each tuple contains: (field_name, (filename, file_object, content_type)) - This gives you more control over the filename sent to the server


πŸ•΅οΈ Combining Files with Form Data

Many upload endpoints require additional metadata alongside the fileβ€”like a description, user ID, or timestamp. You can send both files and regular form fields in the same request.

Combined data pattern: - Use the data parameter for text fields - Use the files parameter for file uploads - Both parameters can be passed to the same request - The library merges them into a single multipart request

Example fields you might send with a file: - description: A text field explaining what the file contains - category: A dropdown selection like "log" or "backup" - timestamp: The current date and time as a string


πŸ§ͺ Comparison: JSON vs. Form Data vs. Multipart

Feature JSON Form Data Multipart Form Data
Data types supported Text, numbers, booleans Text only Text + binary files
File upload support No No Yes
Content-Type header application/json application/x-www-form-urlencoded multipart/form-data
Human readable Yes Partially No (binary encoded)
Use case API data exchange Simple form submissions File uploads with metadata

🧰 Common File Upload Scenarios

Uploading a configuration file: - Open the config file in binary mode - Send it with a field name like 'config' - Include additional fields like 'environment' and 'version' in the data parameter

Uploading multiple log files: - Create a list of file paths to upload - Loop through the list and open each file - Build a dictionary with unique field names or use a list of tuples - Send all files in a single request

Uploading with custom filenames: - Use the tuple format: (field_name, (custom_name, file_object, mime_type)) - This lets you rename the file before sending it to the server - Useful when the local filename doesn't match what the server expects


βœ… Best Practices for File Uploads

Always close your files: - Use the with statement to automatically close files after upload - Example: with open('file.txt', 'rb') as f: ensures cleanup

Set appropriate timeouts: - File uploads take longer than simple requests - Increase the timeout value to accommodate larger files - A timeout of 30-60 seconds is reasonable for most uploads

Handle large files carefully: - For very large files, consider streaming uploads - Monitor memory usage when opening multiple files simultaneously - Some servers have file size limitsβ€”check the API documentation

Check the response status: - Always verify the server response after uploading - A 200 or 201 status means success - 400 or 413 errors often indicate file size or format issues


πŸ” Troubleshooting Common Issues

Server returns 400 Bad Request: - Verify the field name matches what the server expects - Check that the file format is supported - Ensure you're not mixing JSON and multipart incorrectly

File arrives corrupted or empty: - Confirm you opened the file in binary mode ('rb' not 'r') - Check that the file exists and has content before uploading - Verify the server isn't truncating the file

Upload hangs or times out: - Increase the request timeout value - Check your network connection speed - Consider compressing large files before upload

Content-Type mismatch: - Some servers require explicit MIME types - Pass the content type explicitly in the tuple format - Common types: 'text/plain', 'application/json', 'application/octet-stream'


File uploads are a fundamental skill when working with APIs that manage artifacts, logs, or configuration data. Once you understand the multipart format and how to structure your requests, you can integrate file handling into any automation or data pipeline with confidence.


This topic shows how to send files and form data together in a single HTTP request using Python's requests library.


πŸ“€ Example 1: Upload a single text file

This example uploads a simple text file to a server using the files parameter.

import requests

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

file_content = open("example.txt", "rb")
files = {"file": file_content}

response = requests.post(url, files=files)

print(response.status_code)

πŸ“€ Output: 200


πŸ“€ Example 2: Upload a file with a custom filename

This example shows how to override the filename when sending a file to the server.

import requests

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

file_content = open("photo.jpg", "rb")
files = {"image": ("profile_pic.jpg", file_content, "image/jpeg")}

response = requests.post(url, files=files)

print(response.json()["files"]["image"])

πŸ“€ Output: The raw file content as a string


πŸ“€ Example 3: Upload multiple files at once

This example sends two separate files in a single request using a dictionary.

import requests

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

file1 = open("report.pdf", "rb")
file2 = open("summary.csv", "rb")

files = {
    "document": file1,
    "data": file2
}

response = requests.post(url, files=files)

print(response.status_code)

πŸ“€ Output: 200


πŸ“€ Example 4: Combine file upload with form data

This example sends both a file and regular form fields in one multipart request.

import requests

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

file_content = open("invoice.pdf", "rb")
files = {"invoice": file_content}

form_data = {
    "customer_id": "12345",
    "date": "2025-03-20"
}

response = requests.post(url, files=files, data=form_data)

print(response.json()["form"]["customer_id"])

πŸ“€ Output: 12345


πŸ“€ Example 5: Upload a file from memory (string content)

This example sends a file created from a string without saving it to disk first.

import requests

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

file_content = "Hello, this is file content created in memory."
files = {"upload": ("notes.txt", file_content, "text/plain")}

response = requests.post(url, files=files)

print(response.json()["files"]["upload"])

πŸ“€ Output: Hello, this is file content created in memory.


Comparison Table

Feature Example 1 Example 2 Example 3 Example 4 Example 5
Single file upload βœ… βœ… ❌ ❌ βœ…
Custom filename ❌ βœ… ❌ ❌ βœ…
Multiple files ❌ ❌ βœ… ❌ ❌
Form data included ❌ ❌ ❌ βœ… ❌
File from memory ❌ ❌ ❌ ❌ βœ…

When you need to send filesβ€”like logs, configuration backups, or imagesβ€”to a server, standard JSON or form data won't work. Files require a special encoding format called multipart form data. This is how browsers upload files from HTML forms, and it's also how your Python scripts can send files to APIs.


βš™οΈ What Is Multipart Form Data?

Multipart form data is a way to send binary data (files) alongside regular text fields in a single HTTP request. Each piece of data is separated by a boundary marker, allowing the server to distinguish between different parts of the request.

Key characteristics: - Supports both text fields and file uploads in one request - Each part can have its own content type (e.g., image/png, text/csv) - The boundary string is automatically generated and included in the request headers - Required for any API endpoint that accepts file uploads


πŸ› οΈ How to Upload a Single File

To upload a file, you need to open it in binary mode and pass it as part of a dictionary to the files parameter. The server receives the file along with its filename and content type.

Basic file upload pattern: - Open the file using open('filename', 'rb') to read it in binary mode - Create a dictionary with the field name as the key and the file object as the value - Pass this dictionary to the files parameter of your request function - The library handles all the multipart encoding automatically

Example structure for a single file upload: - Import the requests library - Open the file in binary read mode - Store the file object in a dictionary: {'file': open('backup.log', 'rb')} - Send a POST request with files=file_dict - Close the file after the request completes


πŸ“Š Uploading Multiple Files

When you need to send several files at once, you can include multiple entries in the files dictionary. Each file gets its own field name, and the server processes them individually.

Multiple file upload pattern: - Open each file separately in binary mode - Create a dictionary with multiple keys, one per file - Example: {'log_file': open('error.log', 'rb'), 'config_file': open('settings.ini', 'rb')} - Send the request with the entire dictionary as the files parameter - The server receives each file under its respective field name

Alternative approach for lists of files: - Use a list of tuples instead of a dictionary - Each tuple contains: (field_name, (filename, file_object, content_type)) - This gives you more control over the filename sent to the server


πŸ•΅οΈ Combining Files with Form Data

Many upload endpoints require additional metadata alongside the fileβ€”like a description, user ID, or timestamp. You can send both files and regular form fields in the same request.

Combined data pattern: - Use the data parameter for text fields - Use the files parameter for file uploads - Both parameters can be passed to the same request - The library merges them into a single multipart request

Example fields you might send with a file: - description: A text field explaining what the file contains - category: A dropdown selection like "log" or "backup" - timestamp: The current date and time as a string


πŸ§ͺ Comparison: JSON vs. Form Data vs. Multipart

Feature JSON Form Data Multipart Form Data
Data types supported Text, numbers, booleans Text only Text + binary files
File upload support No No Yes
Content-Type header application/json application/x-www-form-urlencoded multipart/form-data
Human readable Yes Partially No (binary encoded)
Use case API data exchange Simple form submissions File uploads with metadata

🧰 Common File Upload Scenarios

Uploading a configuration file: - Open the config file in binary mode - Send it with a field name like 'config' - Include additional fields like 'environment' and 'version' in the data parameter

Uploading multiple log files: - Create a list of file paths to upload - Loop through the list and open each file - Build a dictionary with unique field names or use a list of tuples - Send all files in a single request

Uploading with custom filenames: - Use the tuple format: (field_name, (custom_name, file_object, mime_type)) - This lets you rename the file before sending it to the server - Useful when the local filename doesn't match what the server expects


βœ… Best Practices for File Uploads

Always close your files: - Use the with statement to automatically close files after upload - Example: with open('file.txt', 'rb') as f: ensures cleanup

Set appropriate timeouts: - File uploads take longer than simple requests - Increase the timeout value to accommodate larger files - A timeout of 30-60 seconds is reasonable for most uploads

Handle large files carefully: - For very large files, consider streaming uploads - Monitor memory usage when opening multiple files simultaneously - Some servers have file size limitsβ€”check the API documentation

Check the response status: - Always verify the server response after uploading - A 200 or 201 status means success - 400 or 413 errors often indicate file size or format issues


πŸ” Troubleshooting Common Issues

Server returns 400 Bad Request: - Verify the field name matches what the server expects - Check that the file format is supported - Ensure you're not mixing JSON and multipart incorrectly

File arrives corrupted or empty: - Confirm you opened the file in binary mode ('rb' not 'r') - Check that the file exists and has content before uploading - Verify the server isn't truncating the file

Upload hangs or times out: - Increase the request timeout value - Check your network connection speed - Consider compressing large files before upload

Content-Type mismatch: - Some servers require explicit MIME types - Pass the content type explicitly in the tuple format - Common types: 'text/plain', 'application/json', 'application/octet-stream'


File uploads are a fundamental skill when working with APIs that manage artifacts, logs, or configuration data. Once you understand the multipart format and how to structure your requests, you can integrate file handling into any automation or data pipeline with confidence.

Interactive Views

You are currently in πŸ“š All-in-One mode. Use the tabs at the top to switch to πŸ“– Theory Only or πŸ’» Code Only views.

This topic shows how to send files and form data together in a single HTTP request using Python's requests library.


πŸ“€ Example 1: Upload a single text file

This example uploads a simple text file to a server using the files parameter.

import requests

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

file_content = open("example.txt", "rb")
files = {"file": file_content}

response = requests.post(url, files=files)

print(response.status_code)

πŸ“€ Output: 200


πŸ“€ Example 2: Upload a file with a custom filename

This example shows how to override the filename when sending a file to the server.

import requests

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

file_content = open("photo.jpg", "rb")
files = {"image": ("profile_pic.jpg", file_content, "image/jpeg")}

response = requests.post(url, files=files)

print(response.json()["files"]["image"])

πŸ“€ Output: The raw file content as a string


πŸ“€ Example 3: Upload multiple files at once

This example sends two separate files in a single request using a dictionary.

import requests

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

file1 = open("report.pdf", "rb")
file2 = open("summary.csv", "rb")

files = {
    "document": file1,
    "data": file2
}

response = requests.post(url, files=files)

print(response.status_code)

πŸ“€ Output: 200


πŸ“€ Example 4: Combine file upload with form data

This example sends both a file and regular form fields in one multipart request.

import requests

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

file_content = open("invoice.pdf", "rb")
files = {"invoice": file_content}

form_data = {
    "customer_id": "12345",
    "date": "2025-03-20"
}

response = requests.post(url, files=files, data=form_data)

print(response.json()["form"]["customer_id"])

πŸ“€ Output: 12345


πŸ“€ Example 5: Upload a file from memory (string content)

This example sends a file created from a string without saving it to disk first.

import requests

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

file_content = "Hello, this is file content created in memory."
files = {"upload": ("notes.txt", file_content, "text/plain")}

response = requests.post(url, files=files)

print(response.json()["files"]["upload"])

πŸ“€ Output: Hello, this is file content created in memory.


Comparison Table

Feature Example 1 Example 2 Example 3 Example 4 Example 5
Single file upload βœ… βœ… ❌ ❌ βœ…
Custom filename ❌ βœ… ❌ ❌ βœ…
Multiple files ❌ ❌ βœ… ❌ ❌
Form data included ❌ ❌ ❌ βœ… ❌
File from memory ❌ ❌ ❌ ❌ βœ