Testing a file upload API requires more than confirming that one image reaches a server. This reusable checklist shows how to send multipart requests with cURL and Postman, reproduce browser uploads from the network panel, verify authentication and response contracts, and cover failure cases such as empty, oversized, malformed, and multiple files.
Overview
A typical file upload endpoint accepts an HTTP POST or PUT request with a multipart/form-data body. One part contains the file; other parts may contain metadata such as a title, folder identifier, or document type. The endpoint may also require an access token, an API key, a CSRF value, or a session cookie.
Before testing, write down the contract you expect to verify:
- Method and URL: for example,
POST https://api.example.test/v1/uploads. - File field name: such as
file,document, orattachments[]. - Additional fields: for example,
description,userId, orfolderId. - Authentication: identify whether the request uses a bearer token, API key, basic authentication, or a browser session.
- Success response: record the expected status code, response fields, and file identifier.
- Failure behavior: define how validation, authorization, size, and content errors should be reported.
Keep a small test file in each format your endpoint supports. Use harmless, non-sensitive fixtures with predictable names and sizes. For MIME-type details, compare the request and server expectations with this MIME types and file extensions reference.
Checklist by scenario
1. Basic multipart upload with cURL
The -F option creates multipart form fields. The @ prefix tells cURL to read a local file.
curl --request POST \
--url https://api.example.test/v1/uploads \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--form "file=@./fixtures/sample.pdf" \
--form "description=Upload smoke test"
Use --include when you need to inspect response headers, or --verbose when diagnosing connection and request details. Avoid manually setting Content-Type: multipart/form-data unless you also construct the boundary correctly. cURL normally generates the boundary for a form request.
2. Upload with an explicit content type
If the API validates the part's media type, append a type to the file form value:
curl --request POST \
--url https://api.example.test/v1/uploads \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--form "file=@./fixtures/photo.jpg;type=image/jpeg" \
--form "category=profile-image"
This declares the part type in the request. It does not replace server-side validation of the file's contents, extension, or size.
3. Multiple files
Repeat the same field when the API expects a list under one name:
curl --request POST \
--url https://api.example.test/v1/uploads/batch \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--form "files=@./fixtures/first.txt" \
--form "files=@./fixtures/second.txt" \
--form "folderId=demo-folder"
Some endpoints instead require indexed names such as files[0] and files[1]. Follow the API contract rather than assuming that repeated fields will be interpreted as an array.
4. Authentication variants
For bearer authentication, use an authorization header:
--header "Authorization: Bearer YOUR_ACCESS_TOKEN"
For an API key, use the header name specified by the service:
--header "X-API-Key: YOUR_API_KEY"
For basic authentication, let cURL create the authorization header:
curl --user "USERNAME:PASSWORD" \
--request POST \
--url https://api.example.test/v1/uploads \
--form "file=@./fixtures/sample.pdf"
Do not place real credentials in shell history, shared command snippets, screenshots, or source control. Use environment variables in repeatable tests, for example $UPLOAD_TOKEN, and provide a harmless placeholder when documenting a request.
5. Configure the request in Postman
- Create a new request and select the endpoint's method and URL.
- In the Authorization tab, choose the scheme required by the API. For a bearer token, enter a variable such as
{{access_token}}rather than pasting a long-lived secret into the collection. - Open Body, select form-data, and add a field named exactly as the server expects.
- Change the field type from Text to File, then select a fixture from your machine.
- Add any metadata fields as text values. Confirm spelling, capitalization, and whether values must be strings or numbers.
- Send the request and inspect the status, response body, headers, and request preview.
When using Postman, allow the client to set the multipart content type and boundary. A manually added content-type header can conflict with the generated body and produce a malformed request.
6. Reproduce a browser upload
To investigate a real frontend integration, open the browser's developer tools, select the Network panel, perform the upload, and select the resulting request. Compare the method, URL, query string, request headers, form-data field names, cookies, and response. Use the network panel's “Copy as cURL” option when available, then remove secrets and adapt the command for a safe fixture.
This comparison is useful when a handcrafted request succeeds but the browser fails, or when the browser works while cURL and Postman do not. It can reveal a missing field, a different endpoint, an unexpected cookie, or a frontend-generated header. Review the file uploads integration guide for the relationship between HTML forms, JavaScript, and API requests.
7. Automate a repeatable request
Once the manual request is correct, move it into a script or API test runner. Keep the endpoint, token, and fixture path configurable. A useful automated assertion checks more than a successful HTTP status:
- the status matches the documented success response;
- the response contains a non-empty upload or file identifier;
- the returned filename, media type, and size match the fixture where applicable;
- the upload can be retrieved or queried through the intended follow-up endpoint;
- the response does not expose internal paths, credentials, or unnecessary metadata.
What to double-check
Use this matrix as a minimum API upload test set. Replace the expected outcomes with the contract for your service.
| Scenario | Request variation | What to verify |
|---|---|---|
| Valid single file | Supported type and size | Success status, stable response shape, and stored-file identifier |
| Missing file | Omit the file field | Clear validation error and no partial record |
| Empty file | Use a zero-byte fixture | Accepted or rejected behavior matches the specification |
| Unsupported type | Use a disallowed extension or media type | Safe rejection without trusting the extension alone |
| Oversized file | Exceed the documented limit | Predictable error, useful message, and no incomplete upload |
| Malformed content | Use a damaged or truncated fixture | Content validation occurs before processing or publication |
| Multiple files | Send two or more valid and invalid files | Defined all-or-nothing or per-file result behavior |
| Missing authentication | Remove the token or key | Authorization failure without accepting the file |
| Expired or invalid token | Use a controlled invalid credential | Correct error response and no sensitive details |
Check limits at every layer that handles the request. A reverse proxy, web server, application framework, object-storage adapter, and client may have different limits or timeout behavior. If a request fails before reaching application logs, consult the file upload troubleshooting guide.
Also verify security boundaries: filenames should not become trusted paths, uploaded content should not be published unintentionally, and temporary files should be cleaned up after rejected requests. Use the file upload security checklist alongside functional tests. For browser-facing forms, confirm that labels, error messages, keyboard operation, and progress feedback remain usable; the accessible upload forms guide covers those checks.
Common mistakes
- Sending JSON instead of multipart: A JSON body can describe a file, but it is not the same as transmitting a binary file in a multipart part. Match the endpoint's documented body format.
- Using the wrong field name:
upload,file, andfilesare different fields. Compare the API contract with the browser request. - Overriding the multipart header: Let cURL, Postman, or the browser generate the boundary when they build the form body.
- Testing only a happy path: A successful PDF upload does not establish behavior for an empty, oversized, malformed, or unauthorized request.
- Confusing client and server errors: A browser CORS failure, a proxy size rejection, and an application validation response require different fixes. Capture the status and response layer before changing code.
- Exposing secrets in examples: Redact authorization headers and cookies from copied network requests, logs, Postman exports, and tickets.
- Assuming a 2xx response proves persistence: Where the workflow permits it, verify the returned identifier through a safe follow-up lookup or download test.
When to revisit
Keep this checklist with the endpoint's contract and run it before a release, after authentication changes, when upload limits change, and whenever the storage or proxy layer is replaced. Repeat it when a frontend form is redesigned, a new file type is added, or a staging environment is promoted toward production. It is also worth revisiting after a failed integration, a timeout report, a security review, or a regression involving multiple files.
At the start of each review, confirm the URL, method, field names, authentication scheme, allowed types, maximum size, timeout expectations, response schema, and cleanup behavior. Then run one valid request and the relevant negative cases. Record the command or Postman collection version, fixture names, environment, status, and response summary so another developer can reproduce the result.
Finish with three practical actions: save a redacted cURL example, keep a small fixture set under controlled test data, and automate the cases that protect the most important contract assumptions. If the request still fails, compare it with the browser's network request and work outward through the client, proxy, application, and storage layers instead of changing several variables at once.