How to Transmit Binary Data in REST APIs: Base64 vs. Multipart
When designing RESTful APIs, handling structured data like user profiles, billing settings, and product catalogs is straightforward: you format the data as JSON, set the Content-Type header to application/json, and transmit the payload. Complications arise when an application must handle binary files—such as user avatars, PDF invoices, audio messages, compressed archives, or compiled firmware updates.
Because JSON is strictly a textual format governed by RFC 8259, it cannot parse raw binary octets directly. Developers often reach for the easiest workaround: converting the file into a Base64 string and stuffing it into a JSON property. While this works seamlessly for a 15 KB icon in local testing, in production with large uploads, it can cause memory exhaustion, gateway timeouts, and inflated cloud bandwidth bills. Transmitting binary data across REST APIs requires selecting the right architectural pattern for your workload: Base64 JSON embedding, Multipart Form-Data, Raw Octet Streams, or Presigned Direct-to-Storage URLs.
1. Pattern 1: Base64-Encoded Strings Inside JSON
The most intuitive way to transmit binary data through a REST API is converting the binary buffer into a Base64-encoded ASCII string and assigning it to a standard JSON object property.
"userId": 1042,
"fileName": "avatar.png",
"fileData": "iVBORw0KGgoAAAANSUhEUgAAAAUA..."
}
The Architectural Penalties:
- The 33.33% Size Tax: As detailed in our mathematical proof on how Base64 encoding works, Base64 converts every 3 bytes of binary into 4 text characters, permanently increasing file size by a third.
- Severe Memory Amplification: The backend server must parse the JSON payload, allocate a second string for the Base64 value, and decode it into a binary buffer. Processing a 50 MB upload can cause an immediate 150 MB to 200 MB spike in RAM, crashing container nodes.
- Gateway Limits: Cloudflare and AWS API Gateway enforce strict request body limits (10 MB to 100 MB). The 33% bloat pushes files past these boundaries prematurely.
Verdict: Use Base64 inside JSON only for tiny payloads under 50 KB. To encode and decode test payloads client-side, use our Base64 Converter.
2. Pattern 2: Multipart Form-Data (RFC 7578)
Standardized under **RFC 7578**, multipart requests allow an HTTP client to transmit multiple distinct data blocks—both structured text fields and raw binary streams—inside a single HTTP POST or PUT request:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4
------WebKitFormBoundary7MA4
Content-Disposition: form-data; name="document"; filename="invoice.pdf"
Content-Type: application/pdf
[RAW BINARY OCTETS TRANSMITTED HERE - ZERO OVERHEAD]
------WebKitFormBoundary7MA4--
- Zero Encoding Overhead: The file is transmitted as pure binary. A 10 MB file transmits as exactly 10 MB.
- Streaming Capability: Servers can stream incoming file chunks directly to disk or cloud buckets without buffering the entire file into application RAM.
Verdict: The gold standard for web forms and file uploads between 100 KB and 25 MB.
3. Pattern 3: Raw Binary Streams (`application/octet-stream`)
If an endpoint ingests a single file without accompanying form fields, streaming raw binary directly in the HTTP body is the leanest solution:
Content-Type: application/octet-stream
[RAW UNMODIFIED BINARY BYTES STREAMED DIRECTLY]
Trade-Off: Because the body contains purely binary data, any metadata (such as author ID or upload tags) must be passed via custom HTTP headers or query parameters.
4. Pattern 4: Enterprise Standard — Presigned Cloud URLs
In high-scale enterprise architectures, **application servers should never act as proxies for large file uploads**.
Routing 100 MB files through your API server locks up worker threads and wastes bandwidth. Modern cloud architectures (AWS S3, Google Cloud Storage, Azure Blob) use **Presigned Direct Uploads**:
The 4-Step Presigned Architecture:
- Request: Client requests an upload token from your API:
POST /api/upload-ticket(sends metadata only). - Authorization: Your API generates a temporary, cryptographically signed URL valid for 10 minutes.
- Direct Upload: The client uploads the raw binary file directly to S3 via an HTTP
PUTrequest. Zero bytes touch your API servers. - Completion: A cloud webhook notifies your database that the file landed successfully.
5. Architectural Comparison Matrix
| Method | Bandwidth Overhead | Server RAM Impact | Max Recommended Size | Best Use Case |
|---|---|---|---|---|
| Base64 in JSON | +33.33% Bloat | High (2x–4x RAM) | < 50 KB | Icons, inline signatures |
| Multipart Form-Data | ~0% (Negligible) | Low (Streamed) | 50 KB – 25 MB | Forms with attachments |
| Raw Octet-Stream | 0% (Pure bytes) | Minimal | 1 MB – 50 MB | Service-to-service streaming |
| Presigned Cloud URL | 0% (Pure bytes) | Zero (Bypasses API) | Multi-Gigabyte | Large media, production apps |
6. Security and Validation Controls
- Inspect Magic Bytes: Never trust the client-supplied
Content-Typeheader. Attackers can upload an executable script while setting the header toimage/png. Always inspect the file's leading binary signature bytes on the server. - Verify Checksums: Require clients to calculate an SHA-256 hash before uploading. Verify this checksum upon arrival to ensure data was not corrupted or modified. You can compute checksums directly in your browser using our Hash Generator.
- Sanitize File Names: Never store files using the user-provided filename. Generate a cryptographically random identifier for storage using our UUID Generator.
Frequently Asked Questions
Why does Base64 increase file size by 33%?
Base64 maps binary data into 6-bit characters. Because standard bytes are 8 bits, every 3 bytes of binary requires 4 Base64 characters to represent. That mathematical ratio (4 / 3 = 1.3333) causes a permanent 33.33% size expansion.
Can I stream Base64-encoded strings in Node.js?
Yes, but it is inefficient. Streaming libraries must buffer chunks, manage boundary cuts across 4-character blocks, and convert characters into byte buffers on the fly. Streaming raw binary via multipart streams consumes far less CPU.
What is the MIME type for raw binary data?
The standardized MIME type is application/octet-stream (RFC 2046). It signals to parsers and browsers that the payload consists of arbitrary 8-bit binary bytes that should be treated as a raw download or file stream.
Why shouldn't our API servers handle 100 MB file uploads directly?
Handling large uploads consumes open HTTP socket connections, worker threads, and memory for long durations. A slow mobile client can block an API worker thread for several minutes. Delegating uploads to cloud storage via presigned URLs keeps your API servers responsive for lightweight JSON requests.