Open your browser's network tab on almost any API you've built and look at a response payload. Chances are it's pretty-printed — two-space indentation, line breaks after every key, the works. That's great in Postman. It's dead weight on the wire.
This isn't a "you're doing everything wrong" post. Pretty-printed JSON is easier to read, and readability matters during development. The problem is when that same formatting ships to production unchanged, on every request, forever. It's one of those optimizations that takes five minutes and nobody ever gets around to.
What "bloated" actually means here
JSON has no concept of formatting. {"id":1} and the same object spread across five indented lines are semantically identical — the parser doesn't care. Every space, newline, and indentation level in a pretty-printed payload exists purely for human eyes and costs you bytes for no functional reason.
Here's a single object from an API response, formatted the way most frameworks output it by default:
{
"id": 1,
"uuid": "9c3f0001-a1b2-4e5f-8c9d-000000000001",
"name": "Dev Smith",
"email": "dev.smith@example.com",
"is_active": false,
"created_at": "2026-02-08T10:15:00Z",
"roles": [
"admin"
],
"address": {
"street": "693 Main St",
"city": "Lagos",
"zip": "21395"
}
}
That's 303 bytes. Strip the formatting and you get:
{"id":1,"uuid":"9c3f0001-a1b2-4e5f-8c9d-000000000001","name":"Dev Smith","email":"dev.smith@example.com","is_active":false,"created_at":"2026-02-08T10:15:00Z","roles":["admin"],"address":{"street":"693 Main St","city":"Lagos","zip":"21395"}}
241 bytes. Same data, same structure, 20% smaller. For one object that's nothing. Multiply it across a paginated response with 50 objects and it stops being nothing.
Measuring it on a real payload
I generated a realistic API response — a paginated user list, 50 records, each with an id, uuid, email, a roles array, and a nested address object — and measured it three ways: pretty-printed, minified, and both of those gzipped. Here's the actual output:
Pretty-printed JSON size: 19193 bytes
Minified JSON size: 12594 bytes
Bytes saved by minifying alone: 6599 (34.4% smaller)
Pretty-printed JSON, gzipped: 2050 bytes
Minified JSON, gzipped: 1929 bytes
Bytes saved after gzip too: 121 (5.9% smaller)
Read those two blocks together, because the second one is the part most "minify your JSON!" posts skip, and it matters more than the first.
The gzip question
If your API sits behind a server that gzips responses (which, on HTTPS in 2026, is most of them), a huge chunk of that whitespace savings evaporates. Gzip is extremely good at compressing repetitive characters, and indentation is about as repetitive as text gets. In the numbers above, minifying saved 34.4% uncompressed but only 5.9% once gzip was applied to both versions. The compressed minified file was still smaller — just not dramatically.
So the honest answer to "should I minify my JSON?" is: it depends on where compression happens, and whether it happens at all.
Minification still earns its keep in a few specific situations:
- Uncompressed responses. Not every endpoint goes through gzip/brotli — internal services, some CDNs with caching quirks, and plenty of misconfigured servers serve JSON uncompressed. Here the 34% isn't theoretical, it's what actually goes over the wire.
-
WebSocket and real-time messages.
permessage-deflatecompression exists for WebSockets but isn't always enabled, and a lot of high-frequency message protocols (game state updates, live dashboards, trading data) skip compression entirely because the CPU overhead per tiny message isn't worth it. Minifying costs nothing at send time and shrinks every single frame. - Mobile and constrained networks. Even compressed, smaller is still smaller, and on a flaky connection every byte affects time-to-first-render.
- Storage and logging pipelines. If you're storing millions of JSON blobs — event logs, audit trails, cached API responses — the indentation overhead adds up in disk and in what you pay for it, independent of anything that happens over the network.
- Before compression runs at all. If you minify before gzip instead of after, you're not fighting compression, you're just feeding it less redundant input. It still helps, it just helps less than the raw number suggests.
Where it barely matters: a typical REST API response served over HTTPS with gzip/brotli already on, where response sizes are in the low kilobytes. You're optimizing 6% of an already-small number. Worth doing if it's free (a one-line config change), not worth an engineering sprint.
How to actually do it
This is the easy part, which is exactly why it's worth doing — the cost is close to zero.
JavaScript / Node — JSON.stringify minifies by default. The formatting you see in dev tools comes from the third argument:
// Pretty (for humans, debugging, config files)
JSON.stringify(data, null, 2);
// Minified (for the wire)
JSON.stringify(data);
If your API framework is pretty-printing responses, it's almost always because someone explicitly configured an indent for development and it never got turned off for production. Check your res.json() / serializer config.
Python:
import json
# Default is already fairly compact, but separators removes the extra space
# after commas and colons that json.dumps adds by default
minified = json.dumps(data, separators=(",", ":"))
Command line, with jq:
jq -c . input.json > output.json
-c is compact output — same data, no formatting.
Quick one-off check. If you just want to quickly strip formatting and test a payload without wiring anything into your build — say, you're debugging an API response on the fly and want to strip whitespace immediately — you can paste it into a browser-based tool like the JSON minifier to collapse it instantly, no setup required. Useful for a quick sanity check before you decide whether it's worth automating in your codebase.
For anything that runs repeatedly — API responses, build artifacts, CI output — automate it at the framework or build-tool level rather than doing it by hand. A one-time manual minify on a static file is fine; a manual step in a deploy pipeline is a bug waiting to happen.
Going further: the array-of-objects problem
Whitespace isn't the only source of bloat. If you have a large array of objects that all share the same keys, you're repeating those key names on every single element — and that repetition survives minification, because the keys are still there.
Same 50-record user list, keys only (id, uuid, name, email, is_active, created_at), compared as an array of objects versus a columnar structure (one array per field, aligned by index):
Row-based (array of objects), minified: 8142 bytes
Columnar (object of arrays), minified: 5506 bytes
Saved: 2636 bytes (32.4% smaller)
That's the classic row-based vs. columnar tradeoff:
// Row-based — key names repeated 50 times
[
{"id":1,"name":"Dev Smith","is_active":false},
{"id":2,"name":"Alice Johnson","is_active":true}
]
// Columnar — key names appear once
{
"id": [1, 2],
"name": ["Dev Smith", "Alice Johnson"],
"is_active": [false, true]
}
Interestingly, even gzip didn't fully erase this difference — the compressed columnar version was still about 7.7% smaller than the compressed row-based one in this test, because gzip's window doesn't always catch every repeated key across a large array as efficiently as removing the repetition outright.
This is a real technique used in analytics and data-transfer formats (it's the whole idea behind Arrow and Parquet), but it's a bigger structural change than flipping a minify flag — it breaks the natural "one object per record" shape that most client code expects, so it's worth reaching for only when you're moving genuinely large arrays, not as a default for every endpoint.
Where minification bites you
A few things to watch for before you apply this everywhere:
- Don't minify anything a human needs to read or diff. Config files, fixtures, seed data, anything committed to git — keep those pretty-printed. A minified config file that breaks in production is miserable to debug, and a minified JSON file in a pull request makes every diff useless.
- It doesn't fix ambiguous data. Duplicate keys in an object, inconsistent types across an array, deeply nested structures that should've been flattened — minifying changes byte count, not data quality. If your JSON has structural problems, minifying just makes them smaller and harder to spot.
-
Comments aren't valid JSON either way. If you're relying on
// commentsin a.jsonfile for documentation, that's actually JSON5 or JSONC, not JSON — a different problem than formatting, but worth knowing minification tools won't preserve those comments if you run them against a strict JSON parser. -
Logs are for reading later. If you're minifying application logs to save disk space, fine — but make sure whatever reads them back (log viewer,
jq, alerting rules) can still parse compact JSON. Most can; some older tooling assumes one key-value pair per line.
The actual checklist
- Production API responses: minify. It's a one-line config change in most frameworks, so there's little reason not to, even where gzip is already doing most of the work.
- Real-time / WebSocket / streaming messages: minify, especially if compression isn't enabled on that channel.
- Config files, fixtures, anything committed to source control: keep pretty-printed.
- Large arrays of uniform objects moving significant data: consider a columnar structure, but only when the size actually justifies the added complexity on the receiving end.
- Before optimizing further: check whether gzip/brotli is even on. If it's off, turning it on will usually save you more than minification alone, and the two aren't mutually exclusive — do both.
None of this is exotic. It's a serializer setting, an output flag, or in the worst case a five-minute script — the kind of change that's easy to skip because it never feels urgent, right up until someone finally looks at what's actually going over the wire.
Top comments (0)