Guide
Sending file uploads as Base64 in a JSON API payload
JSON has no native way to carry binary data. Base64 is the workaround — and workarounds have costs.
By Buğra SözeriPublished
JSON’s data model — defined in RFC 8259 — has no native binary type. Every value is either a string, number, boolean, null, object, or array, and strings must be valid Unicode text. A raw file’s bytes can contain sequences that aren’t valid text at all, so there’s no way to drop them into a JSON string directly. Base64 solves that by re-encoding the binary as a restricted alphabet of 64 printable ASCII characters — safe to embed in a JSON string, an XML document, or a URL, at the cost of roughly 33% more bytes than the original file.
What the payload actually looks like
A typical file-upload JSON body wraps the encoded data in a field alongside metadata the server needs to reconstruct the file:
{ "filename": "invoice.pdf", "contentType": "application/pdf", "data": "JVBERi0xLjQK..." }
The server decodes the data field back to raw bytes on receipt — the exact reverse of the encoding step — and writes it to storage or processes it directly. This pattern is common in webhook payloads and API clients that only want to speak JSON, since it avoids the extra content-type negotiation that multipart uploads require.
The tradeoff: size and request limits
| Approach | Size overhead | Best for |
|---|---|---|
| Base64 in JSON | ~33% larger | Small files, JSON-only clients, webhooks |
| multipart/form-data | None (minimal boundary overhead) | Large files, standard upload endpoints |
Because most API gateways, load balancers, and server frameworks enforce a maximum request body size, the 33% overhead directly eats into how large a file can actually be uploaded. A gateway capped at 10MB effectively caps Base64-in-JSON uploads at around 7.5MB of real file content — worth checking against your provider’s documented limits before assuming a file that’s “under the cap” will actually go through once encoded.
When to skip Base64 entirely
For anything beyond a few megabytes, or any endpoint where request size or upload speed genuinely matters, multipart/form-datais the better tool: it sends the file’s raw bytes with only a small boundary-marker overhead, no 33% tax. This is also what plain HTML <input type="file"> forms use natively via the FormData API. Reach for Base64-in-JSON specifically when the client can only send JSON, the file is small, or the payload needs to stay self-contained as a single JSON object — not as a default for every upload.
Frequently asked questions
- Why can't I just put binary data directly in JSON?
- JSON's grammar only defines strings, numbers, booleans, null, objects, and arrays — strings must be valid Unicode text. Raw binary bytes can contain byte sequences that aren't valid text at all, so they can't be embedded in a JSON string without first converting them to a text-safe representation, which is exactly what Base64 does.
- What's the size cost of Base64-encoding a file for a JSON API?
- About 33% larger than the raw file, plus whatever JSON string-escaping adds on top (usually negligible since Base64's alphabet avoids characters that need escaping). A 3MB PDF becomes roughly 4MB of Base64 text inside the JSON body.
- When should I use multipart form data instead of Base64 in JSON?
- For large files, or any upload where request size matters — multipart/form-data transmits the file as raw bytes with no encoding overhead, and is the standard approach for file upload endpoints (including plain HTML forms). Base64-in-JSON is more common for smaller files, API clients that only speak JSON, or webhook payloads that need to stay self-contained in one JSON object.
- Does Base64 in JSON count against request size limits?
- Yes, and the 33% overhead makes it count faster. If an API gateway or server caps request bodies at, say, 10MB, a Base64-encoded file has to be under about 7.5MB raw to fit — worth checking before assuming a file 'under the limit' will actually upload successfully once encoded.
Sources & references
Authoritative references cited by this piece. Verified by Buğra Sözeri on the dates shown and re-checked at every deploy.
- RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format — The JSON spec, which defines only text-based string values — no native binary type(as of )
- RFC 4648 — The Base16, Base32, and Base64 Data Encodings — The encoding used to represent binary as a JSON-safe string(as of )
- MDN — Using FormData objects — The multipart alternative that avoids the Base64 overhead for file uploads(as of )
Related
More guides on this topic
- JSON vs YAML: Choosing the Right Config FormatA side-by-side comparison of JSON and YAML for configuration files — syntax, comments, anchors, strict parsing, the Norway problem, JSON's number-precision trap, and a decision tree for picking one.
- Cryptographic Hashing Explained: MD5, SHA-1, SHA-256, SHA-512What a cryptographic hash actually does — the three properties that matter, why MD5 and SHA-1 are dead, where SHA-2 and SHA-3 fit, and why bcrypt/Argon2 exist instead of hashing passwords with SHA-256.
- Regex Cheat Sheet: Common Patterns Every Developer NeedsTwenty-five battle-tested regex patterns — email, URL, IPv4, IPv6, ISO date, UUID, semver, hex color, US and international phone, slug — plus the quantifier, lookaround, and flavour notes that make them portable.
- JWT Tokens: How to Decode, Verify, and Avoid the Common MistakesA practical guide to JSON Web Tokens — three-segment structure, base64url encoding, the standard claims, HS256 vs RS256 vs ES256, the alg-none attack, refresh-token patterns, and when JWT is the wrong tool.
Published September 25, 2026