v5.0
live available
devtools guide 07 Oct 2026

Seven JSON parsers on one 621 MiB file, ranked by peak memory

Same file, same sum, seven parsers. Peak memory ran from 3 MiB to 5 GiB, and the one that gave up had 27 GiB of RAM free when it did.

By Purvansh Parmar Checked 2026-10-21 Read 8 min Entries 7

You have a 621 MiB JSON export and one number to get out of it. Seven tools can do that. The spread between them is not the twenty percent a benchmark table would suggest, it is three orders of magnitude of memory, and one of the seven cannot do it at all on a machine with 27 GiB free. The thing that decides the answer is not how fast a parser scans bytes. It is whether teh parser builds the whole document in memory before you are allowed to look at any of it.

How this was checkedGenerated two JSON arrays of the same shape, 164 MiB and 621 MiB, and made seven parsers compute the same sum over one float field. Measured wall clock as the median of three runs and peak RSS with /usr/bin/time on an 8 thread i5-1135G7 with 31 GiB of RAM. Node 22 could not read the larger file at all. orjson, which advertises lower memory use when fed bytes, peaked 1.45 GiB higher than the standard library on the same input.

What I compared on

01 jq --stream

The same jq binary told to emit path and value events instead of building the document.

Wins
The job fits anywhere. A 128 MiB container runs this against a file five times its own memory limit, and the same command works unchanged when the file doubles.
Loses
It is 3.6 times slower than the jq you already know, and the filter is genuinely hard to write: selecting one field means matching on path arrays, which is unreadable next to map(.amount).
3 MiB peak57.4 s3.6x slower than jq

02 ijson

Python event driven JSON parser, with a C backend over yajl2 when one is available.

Wins
The only entry here with both constant memory and a readable API: ijson.items(f, 'item.amount') is one line and does what it looks like. The C backend is doing real work, and it is the default when the wheel installs cleanly.
Loses
It is a dependency, and the wheel matters: the pure Python fallback backend is several times slower, and nothing warns you which one you got unless you check ijson.backend yourself.
12 MiB peak7.3 sconstant across a 3.8x file size increase

03 Bun 1.3.14

JavaScript runtime using JavaScriptCore rather than V8, running the same readFileSync and JSON.parse.

Wins
Nothing in the script changes. The same four lines that fail on Node 22 run on Bun, which makes it the cheapest fix available when you hit the ceiling at two in the morning.
Loses
Peak memory is still 2.6 times the file, so this buys you one doubling of input and no more. A 1.5 GiB working set to add up a column is a lot to ask of a build agent.
3.21 s1596 MiB peak2.6x the file size

04 Python json (stdlib)

The standard library parser, json.load straight onto a file object, no dependency at all.

Wins
It is already installed, it is the baseline every other Python number gets quoted against, and for files in the low hundreds of megabytes the difference between this and the fast alternative is twelve percent.
Loses
5.9 times the file in peak memory, and it is the slowest of the in-memory Python options. On anything above about 2 GiB of input you are sizing the machine around the parser.
13.5 s3643 MiB peak5.9x the file size

05 jq 1.6

The default mode of the JSON tool that ships with Ubuntu 22.04, building the whole document first.

Wins
map(.amount)|add is the shortest correct expression anyone wrote all day, and it needs no language runtime, no dependency and no script file.
Loses
It scales its memory exactly with the input and gives no warning before it does. It is also the slowest in-memory option measured here, slower than Python on the same file.
15.8 s3825 MiB peak6.2x the file size

06 orjson 3.13.0

Rust JSON library for Python, the usual recommendation when the standard library is too slow.

Wins
It is genuinely faster, and if your payloads are small and numerous, which is the shape its benchmarks use, the per call overhead it removes is the thing that was costing you.
Loses
On one large document it is the worst choice on the board. The README recommends passing bytes for lower memory use, which is exactly what this test did, and it still peaked 8.2 times the file size.
5096 MiB peak1.13x faster parse8.2x the file size

07 Node 22 JSON.parse

V8's JSON.parse behind readFileSync, the shape of every quick script written in the last decade.

Wins
On files under the ceiling it is second only to Bun, and 515 MiB for a 164 MiB document is the second tightest of the in-memory options.
Loses
The failure has nothing to do with memory and cannot be configured away: 27 GiB were free, and the limit is V8's maximum string length, so it fails identically on a machine with a terabyte.
512 MiB ceiling536,870,888 characters27 GiB free when it failed

Side by side

621 MiB filePeak RSSMultiple of file
jq --stream57.4 s3 MiB0.005x
ijson7.3 s12 MiB0.02x
Bun 1.3.143.2 s1596 MiB2.6x
Python json13.5 s3643 MiB5.9x
jq 1.615.8 s3825 MiB6.2x
orjson 3.13.011.8 s5096 MiB8.2x
Node 22failedn/an/a

Verdict

Use ijson. It held 12 MiB flat across a 3.8x increase in file size, finished the big file in 7.3 seconds, and the call that does it is one line you can read. The in-memory parsers are not wrong, they are just priced per byte of input, and that price is invisible until the day the export doubles and your job gets killed in a container you sized last year. If you cannot add a dependency, jq --stream does the same trick for 3 MiB and a much worse filter.

Sources