JSON can't carry raw binary. So when an API needs to return a generated invoice, a signed contract, or a report inside a JSON response, the PDF gets base64-encoded into a string field. It's a common pattern - and a well-understood set of trade-offs, if you make them deliberately.
The basic pattern
Encode the PDF's bytes as base64 and put the resulting string in a field, alongside enough metadata for the client to handle it:
- - **filename** - what the client should save it as
- **mimeType** - application/pdf
- **data** - the base64 string itself
On the client, decode the string back to bytes and either save the file or hand it to a viewer.
Decision 1: raw base64 or data URI?
A data URI (data:application/pdf;base64,JVBERi...) is convenient when the consumer is a browser - you can assign it directly to a link's href or an embed's src. Raw base64 is cleaner for server-to-server APIs, where the prefix is just noise the other side has to strip. Pick one, document it, and never mix the two in the same API - mismatched expectations here are the single most common cause of "the PDF won't open" bug reports. (If you're debugging one of those right now, see our guide to broken base64 PDFs.)
Decision 2: is base64 the right transport at all?
Base64 inflates size by roughly 33% - every 3 bytes of PDF becomes 4 characters of text. For a 200 KB invoice that's irrelevant. For a 40 MB scanned document it means a 53 MB JSON body that the client has to hold in memory and parse as one string. Rules of thumb:
- - **Under a few MB:** base64 in JSON is fine and keeps your API simple
- **Larger, or many files:** return a download URL instead and serve the PDF as a normal binary response with Content-Type application/pdf
Things that bite in practice
- - **JSON escaping is a non-issue** - base64's alphabet contains no characters that need escaping in JSON strings. If you're seeing backslashes in the payload, something upstream double-serialized the JSON.
- **Line breaks are an issue** - some encoders wrap base64 at 76 characters by default (MIME style). Emit it as one unbroken string.
- **Compression barely helps** - base64 text compresses poorly compared to the original binary, so gzip on the response recovers only part of the 33% overhead.
- **Validate before you ship** - the string should start with JVBERi (the encoding of %PDF) and its length should be a multiple of 4.
Testing your endpoint by hand
When you need to eyeball a payload, paste the data field into our Base64 to PDF converter - it strips any data URI prefix, decodes in your browser, and shows you immediately whether the bytes are a valid PDF. Building test fixtures the other way, PDF to Base64 turns any local PDF into a string, with or without the data URI prefix. Both tools are fully client-side: request payloads containing real customer documents never leave your machine.