Some APIs need to offer an operation to convert a particular file format to another, e.g. converting a TIFF to a PNG. This doesn’t fit the typical JSON-based request common with REST-based APIs, so let’s look at a few approaches to file uploads.
This is a multi-part series on API design guidance, where we take a look at tricks and hidden troubles in API designs and how to avoid them. While these articles may not be exhaustive, they will serve to identify common patterns and anti-patterns in API design. The full series includes:
How to send image files in REST API
If your file upload API issues relate to images, there is a simple solution – direct file uploads. You can use this to solve the problem of how to send an image file in a REST API. It’s a neat solution that doesn’t add too much overhead and is the simplest of our REST API file upload solutions to implement, covering most use cases.
However, not all RESTful API file upload needs can be met by direct file uploads. If you need to implement a REST API upload for multiple files, for example, or handle a REST upload file with metadata, then you will need a multipart solution. We’ve provided a couple of different options below, so that you can select the method that best meets your needs. As such, if you’re wondering how to pass a multipart file in a REST API, read on. Naturally, we abide by REST API file upload best practices in each instance, so that you can pass multipart files with minimal hassle and maximum efficiency.
File upload options: Summary
We’ll discuss these options in greater detail below, but first let’s look at a summary of using three different approaches:
- Director file upload
- Multipart HTTP request
- Two-step metadata + upload
Direct file upload | Multipart HTTP request | Two-step metadata + upload | |
HTTP method | PUT | POST | POST (first step), PUT (second step) |
Use case | Simple file upload (single file) | Multiple files, or file + metadata in a single request | Complex workflows with metadata and separate file upload |
Content-type | Specific to file type (e.g., image/jpeg) | Multiple images or a combination of images and JSON metadata | application/json (metadata) and image/jpeg (file) |
Metadata handling | Not supported | Can handle file + metadata together | Metadata is sent first, then file uploaded separately |
Complexity | Low (simple and direct) | Medium (requires multipart parsing) | High (multi-step process, requires link following) |
Server processing | Simple (file stored directly) | More complex (need to parse multipart data) | Complex (need to process metadata and file separately) |
Error handling | Simple errors (e.g., 400 for invalid file type) | Can have errors on file parsing, multipart format | Multiple points of failure (metadata, file upload) |
Support for file size limits | Depends on server configuration (e.g., max upload size) | May require chunking or limits based on multipart size | Same as multipart, but with potential for larger files (multiple steps) |
Pros | Easy to implement, minimal overhead | Can handle multiple files and metadata in one request | RESTful approach, clear separation of concerns, better for complex APIs |
Cons | Limited to single file, no metadata | More complex implementation, multipart parsing required | More complex interaction, requires handling multiple steps |
Direct file upload
The way to do it is by using the HTTP Content-Type
header on the request to set the proper content. Rather than JSON, the client may push a JPG, PNG, PDF, TIFF, or other binary content to the API directly using the PUT
method:
PUT /profile/1234/image HTTP/1.1
Content-Type: image/jpeg
Content-Length: 284
raw image content
This is the most straightforward method of accepting uploaded content and is recommended for most cases. That said, the value of the direct file upload will depend on precisely what it is you need to achieve.
Option #2: Multipart HTTP request
If the API must support uploading multiple files at once or supporting associated metadata in the same request, the HTTP multipart content approach is recommended. HTTP multipart content is a formal specification that allows requests to upload multiple images or a combination of images and JSON metadata:
POST /profile/1234/images HTTP/1.1
Content-Type: multipart/form-data; boundary=MultipartBoundry
Accept-Encoding: gzip, deflate
--MultipartBoundry
Content-Disposition: form-data; name="image"; filename="12348024_1150631324960893_344096225642532672_n.jpg"
Content-Type: image/jpeg
rawimagecontent.goes.here
--MultipartBoundry
Content-Disposition: form-data; name="category"
my-category
--MultipartBoundry
Content-Disposition: form-data; name="location"
23,-75
--MultipartBoundry--
As you can see, composing requests by the consumer and parsing them on the server may not be as trivial as a typical HTTP request with a single content type. However, there are usually helper libraries that ensure that both the request and response may be reliably parsed and validated with minimal effort.
Option #3: Two-step metadata + upload
If the typical API client interaction needs multiple steps, this option is useful. Below is an example of submitting image meta data first using the POST
method and a 201 Created
response:
POST /profile/1234/image HTTP/1.1
Content-Type: application/json
Content-Length: 284
{ "tags": [...], ... }
The response includes a new resource representation for the metadata submitted at creation, along with a hypermedia link of where to upload the image:
HTTP/1.1 201 Created
Location: /profile/1234/image
{
"id": ...,
"tags": [...],
"_links": [
{"rel":"self", "url":"/profile/1234/image" },
{"rel":"imageUpload", "url":"/profile/1234/image/file" },
...
]
}
This is a more RESTful way to approach the process, as hypermedia links help to drive the workflow as a multi-step process:
PUT /profile/1234/image/file HTTP/1.1
Content-Type: image/jpeg
Content-Length: 284
raw image content
However, the multi-step process prevents a single transactional wrapper around the file upload process and may also be more complex than your target audience would prefer.
Error management
In general terms, the more complex a process is, the more scope there is for error. With file uploads, the error response patterns you see will depend on the approach you take, and your error management strategy will be crucial to the success of that approach. Remember that how you handle errors will impact the overall robustness of your API. Let’s look at some key considerations:
Status code implementation
You can use HTTP status codes to help clients understand why errors are occurring and how to respond to them. Be clear and consistent with your status codes, so that clients know at which point in the process the error is occurring and what action they should take as a result. Examples of status codes include:
-
- 2xx success codes: 200 OK and 201 Created (for a new resource) should be fine for simple upload processes. For more complex processes, using a 202 Accepted to acknowledge process initiation may be helpful, followed by further updates as appropriate.
- 4xx client error codes: Everything from bad formatting to unsupported file types can disrupt things on the client side. Use 400 Bad Request status codes for such cases. You can also use a 413 Payload Too Large code if the file size limit exceeds your bandwidth or a 422 Unprocessable Entity if validation fails.
- 5xx server error codes: You can use 500 Internal Server Error, 502 Bad Gateway and 503 Service Unavailable to communicate everything from database or endpoint unavailability to internal processing errors. Remember to be clear in communicating what the issue is.
Error response patterns
Providing clients with a structured, informative response when an error occurs helps developers take appropriate corrective action swiftly. Be sure that your standard error response contains details of what went wrong and why. For example:
“The file size exceeds the maximum limit of 10MB.”
For certain errors, you could include links to your documentation, to help developers take the corrective actions they need to resolve the issue.
Recovery strategies
When there are multiple potential failure points in your file upload process, providing a clear recovery path for clients is crucial. Let’s look at some examples of issues and appropriate recovery strategies to see this in action:
- File size issues: When a file exceeds your size limit, include the maximum permissible file size in the error response, along with a suggestion of compression or chunking the file into smaller parts.
- Invalid file types: List valid file formats in the error response so the client can retry the upload using an accepted format.
- Metadata errors: For multipart or two-step upload approaches where metadata is missing (or invalid), use error responses to suggest the client submit (or resubmit) the necessary metadata when retrying the upload. Include helpful suggestions regarding what is missing or malformed.
Retry mechanisms
Uploads can fail for a wide range of reasons, from temporary network glitches to server overloads, particularly where large file sizes are concerned. You can implement a retry mechanism to help deal with these failures, enabling clients to attempt a failed upload again automatically.
That said, you need to be careful that your retry mechanisms don’t overwhelm your server – you don’t want to create a self-inflicted denial of service attack! Be sure to implement exponential backoff to avoid this. This will gradually increase the time between each retry, providing the server with time to recover from whatever transient issue was impacting it.
Failing to secure APIs from upload vulnerabilities
Not every file uploaded will be what you expect. Some file uploads may be trying to inject exploits that allow malicious parties to bypass authentication and access internal or user resources. Always validate the content properly before making it available, especially for profile image uploads or theme customization files such as CSS/HTML/JS. This validation is crucial to the security of your API.
Submitting a URL for the file’s location is recommended by some API designers, but has a severe security drawback – the potential for server side request forgery (SSRF). SSRF enables a malicious attacker to perform network scans, view internal resources and other exploits. Use caution if you decide to support external URLs to be used to fetch content. For more on this exploit, check out the article, What is the Server Side Request Forgery Vulnerability & How to Prevent It.
Be sure to apply the proper OWASP file upload guidelines for file uploads to prevent introducing vulnerabilities for what should be a simple file conversion service. Encrypt, validate and secure everything you can to protect your API.
Wrap up
While many APIs offer JSON or XML-based content types, the HTTP specification provides affordances for supporting multiple content types within the same HTTP API. By using content negotiation and an easy-to-understand design, your API can support file uploads easily.
If you’ve still got no APIs at all, check out our step-by-step guide to creating them.