TOON vs JSON, measured against the right baseline
The headline number depends almost entirely on which JSON you compare against — and most published comparisons pick the flattering one.
Before comparing anything, decide what you are comparing against. Almost every
published TOON comparison measures against indented JSON — the output of
JSON.stringify(data, null, 2), with two spaces of padding on every line.
Nobody sends that to a model. Every serialiser in every language emits compact JSON by default; the indentation exists for humans reading a file. Comparing against it inflates every saving by roughly 30 percentage points, for free, before the format has done anything.
Here is the same data measured both ways:
| Payload | TOON | JSON | CSV | Difference | Cheapest |
|---|---|---|---|---|---|
| API response | 210 | 353 | — | −41% | TOON |
| Plain table | 195 | 338 | 163 | −42% | CSV |
| Nested config | 81 | 67 | — | +21% | JSON |
| Records with a list | 290 | 218 | — | +33% | JSON |
Take the API response row. Against indented JSON, TOON looks like it saves around two thirds. Against minified JSON — the thing that actually goes into the prompt — it saves about 40%. Both numbers are arithmetically correct. Only one of them describes a decision you might make.
Where the savings come from
JSON repeats every key in every object. In an array of 14 records with 5 fields
each, the string "name" appears 14 times, "active" 14 times, and so on —
70 key repetitions carrying five keys’ worth of information.
TOON declares the keys once as a header and streams the values as rows:
data[14]{id,name,role,signups,active}:
1001,user_0,admin,10,false
1002,user_1,member,13,true
The saving scales with the number of records, because the header cost is paid once regardless. Two records amortise it poorly; two hundred amortise it well. This is also why a small payload shows a percentage that means very little — the header is a fixed cost against a small base.
Where JSON wins
The mechanism above needs a repeated key set of primitive values. Remove either condition and TOON has nothing to compress:
- Nested structures — a configuration tree has no repetition to amortise. The nested config row above costs more in TOON than in minified JSON.
- Non-primitive fields — one array field per record disables the tabular form entirely, and the encoder falls back to repeating every key. The “Records with a list” row shows the result: TOON loses by about a third.
Neither of these is an edge case. API responses containing nested objects are ordinary, and so are config payloads.
What about accuracy?
The published benchmarks report that models read TOON about as accurately as JSON — a percentage point or two either way depending on the model and the task, which is inside the noise of most evaluations. The interesting claim is not that TOON is more accurate; it is that it is not less accurate while using fewer tokens, on the shapes where it uses fewer tokens.
Worth knowing: smaller and cheaper models — exactly the ones where token savings matter most — are the ones most likely to lose accuracy on complex queries over less familiar formats. If you are optimising a pipeline running on a small model, evaluate accuracy on your own task rather than assuming parity.
A fair comparison, in four lines
If you want to redo this measurement on your own data:
- Minify the JSON.
JSON.stringify(data), no second argument. - Encode the same data as TOON.
- Count both with the tokenizer of the model you actually use — not a character count, and not a different model’s tokenizer.
- Subtract the tokens your prompt spends explaining the format.
Step 4 is the one everyone skips, and on short prompts it decides the answer.
The converter does the first three for any payload you paste, and states the fourth as a caveat rather than quietly omitting it.