Paste 500 rows of JSON into a prompt and count what you're actually paying for. Every row repeats the same field names in quotes. Every object carries its own pair of braces. Commas, colons, and whitespace stack up between them. The model is billed per token for all of it, and most of those tokens carry no information your data didn't already imply. For a uniform array, the structure is identical on every line — yet JSON re-declares it on every line anyway.
That repetition is the specific inefficiency TOON — Token-Oriented Object Notation — is built to remove. It's a lossless, human-readable serialization format designed for one job: feeding structured data to a language model with as few wasted tokens as possible. It keeps JSON's data model intact, so round-trips are deterministic, but it drops the syntax that a model doesn't need to see twice.
Declare the shape once
The core move is simple. Where JSON restates keys and delimiters for every element of an array, TOON declares the shape once in a header and then streams bare rows underneath it. Nested objects use YAML-style indentation; uniform arrays of objects collapse into a CSV-like table.
Here's the before:
{
"users": [
{ "id": 1, "name": "Alice", "role": "admin" },
{ "id": 2, "name": "Bob", "role": "user" }
]
}
And the after:
users[2]{id,name,role}:
1,Alice,admin
2,Bob,user
Two things in that header are doing real work. The [2] declares how many rows to expect. The {id,name,role} names the columns exactly once. Everything below is pure data. Scale the array to hundreds of rows and the savings compound: you pay for id, name, and role a single time instead of once per record.
Those aren't just space savings — they're guardrails. The explicit length and the column header give the model something to validate against. It knows how many rows should follow and what each field is called, which is why the format tends to help retrieval rather than merely shrink the payload.
What the benchmarks actually show
The project publishes its own benchmarks, and the honest reading is that the win is real but conditional.
On flat, tabular data — the format's best case — TOON encoded a dataset in 67,778 tokens where JSON needed 164,452, a 58.8% reduction. That's within about 6% of raw CSV, which is roughly the cost of the structural metadata TOON adds back on top. Individual workloads land in the same range: a time-series analytics set came in at 59.0% fewer tokens than JSON, a dump of GitHub repository data at 42.3%.
The more interesting result is accuracy. Across a mixed-structure benchmark, TOON used 39.9% fewer tokens than JSON while scoring 76.4% on retrieval questions against JSON's 75.0%. Measured as accuracy per thousand tokens, that's 27.7 for TOON versus 16.4 for JSON. Per model, the pattern held across the board:
| Model |
TOON |
JSON |
| Claude Haiku |
59.8% |
57.4% |
| Gemini 3 Flash |
96.7% |
96.7% |
| GPT-5 Nano |
90.9% |
89.0% |
| Grok 4.1 Fast |
58.4% |
56.5% |
Fewer tokens and equal-or-better answers is not the tradeoff most people expect from a compression scheme. The explanation is that the structure TOON makes explicit — row counts, named columns — is exactly the structure a model otherwise has to infer from repeated punctuation.
Try it before you commit
You don't need to adopt anything to measure it. The CLI runs through npx and can print the token delta directly:
# Convert and see the token statistics
npx @toon-format/cli data.json --stats
# Pipe from stdin
cat data.json | npx @toon-format/cli -o data.toon
If you'd rather wire it into an application, the encoder is a library call. Official and community implementations exist for TypeScript, Python, Rust, Go, Java, Swift, and .NET, so it fits wherever your prompt-assembly code already lives:
import { encode } from '@toon-format/toon'
const prompt = encode({ users: [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
]})
One practical tuning knob worth knowing: the delimiter is configurable, and the choice is written into the array header so the format stays self-describing. Tabs often tokenize better than commas because a tab is a single token that rarely collides with natural-language text, so items[3 ]: with tab separators can edge out the comma default on some tokenizers. Measure it against your own model rather than assuming.
Where it stops paying off
The format is honest about its limits, and so should you be. TOON's advantage comes from uniformity, so it evaporates when the data isn't uniform.
Deeply nested structures with little tabular content are where plain JSON is often the more efficient choice — there's no repeated schema to factor out, and TOON's indentation adds overhead instead of removing it.
Semi-uniform data in the awkward 40–60% tabular range is the weakest case: not regular enough to tabulate cleanly, not irregular enough to leave alone. Pure flat tables with no nesting are marginally better served by raw CSV, which is about 5% smaller because it carries no structural metadata at all. And on latency-critical paths — especially quantized or edge models — compact JSON is sometimes decoded faster, so benchmark locally before assuming smaller means quicker.
The takeaway
Look at your prompts and find the arrays. If you're sending a language model uniform lists of records — user rows, log lines, catalog entries, time-series points — you're very likely paying a JSON structure tax of 30 to 60% on those payloads, and getting nothing for it. Run one of those payloads through npx @toon-format/cli --stats this week. If the token delta is large, converting is a few lines at the boundary where you build the prompt, and the benchmarks suggest your retrieval accuracy holds or improves on the way down. If the delta is small, your data isn't uniform enough to benefit, and now you know that too — for the cost of one command.
Sources: TOON — Token-Oriented Object Notation (GitHub)