What Is a JSON Token Counter, and Why It Matters for LLM APIs

If you’ve sent JSON to an LLM API and gotten a bill or a context-limit error that seemed way too big for the data you actually sent, this is why. LLMs don’t read text the way we do. They break it into chunks called tokens first, and JSON happens to be one of the more token-expensive ways to represent the same information. Let’s get into what that actually means and what you can do about it.

What a Token Actually Is

A token isn’t a word, and it isn’t a character either. It’s somewhere in between, a chunk of text that a language model’s tokenizer decided to treat as one unit. Common whole words often become a single token. Longer or less common words get split into a few pieces. Punctuation, quotes, and brackets frequently become their own tokens too.

That last part matters a lot for JSON specifically. Every {, every ", every :, every , is potential token overhead that plain prose or a lighter format just wouldn’t have.

json token counter
A json token counter shows what LLM APIs actually charge for. We measured why JSON’s repeated key names quietly waste tokens, and how to fix it.

Why This Matters for LLM APIs

Two reasons, and they’re both practical, not academic.

You’re billed per token, in and out, for most LLM APIs. A JSON payload that’s more token-heavy than it needs to be costs you real money at scale, even if the actual information inside it is small.

Context windows are measured in tokens, not characters or “amount of data.” Send a large, verbose JSON blob and you can burn through a meaningful chunk of your available context before the model’s even gotten to your actual question.

A Quick, Honest Note on Measuring This

We wanted to give you real tested numbers here, not vibes. The catch: the actual tokenizer libraries these models use need to download their vocabulary data from OpenAI’s own servers, and that wasn’t reachable from where this was tested. So instead of faking a token count we didn’t actually run, we measured character count instead, which is a well-known, honest stand-in. Tokenizers roughly track text length (English text averages somewhere around 4 characters per token), so a structure that’s shorter in characters is reliably shorter in tokens too, even if the exact number shifts a bit depending on which model’s tokenizer you’re using. Treat the percentages below as directional, not as an exact quote from any specific model.

The Test: Same Data, Three Structures

We took the same 50 records, structured three different ways, and measured the character count for each.

Pretty-printed, one object per record with full key names repeated every time:

[
  { "id": 1, "name": "Alex", "role": "admin", "active": true },
  { "id": 2, "name": "Sam", "role": "editor", "active": true }
]

Minified, same structure, whitespace stripped:

[{"id":1,"name":"Alex","role":"admin","active":true},{"id":2,"name":"Sam","role":"editor","active":true}]

Flattened, key names stated once in a header, then just the values:

{"columns":["id","name","role","active"],"rows":[[1,"Alex","admin",true],[2,"Sam","editor",true]]}

Here’s what came out of it, at 50 records:

FormatCharactersReduction vs. pretty
Pretty-printed4,235
Minified2,73435.5%
Flattened (header + rows)1,38367.3%

Minifying alone gets you a solid win, which lines up with what we found testing minification for other reasons in our minify JSON guide. But look at the flattened version. Cutting the repeated key names, which is the part almost nobody thinks about, got us all the way to a 67% reduction against the original, and roughly 49% smaller than even the minified version. That gap only grows as the row count grows, since you’re paying for “id”, “name”, “role”, and “active” as literal text, over and over, once per record, in the standard array-of-objects shape.

Why This Happens

Arrays of objects are the most natural way to represent a list of records in JSON, and also the most wasteful when you’re optimizing for an LLM specifically. Every single record repeats every single key name in full. Fifty records means the word “active” shows up fifty separate times as plain text, contributing nothing new each time beyond the actual true/false value sitting next to it.

A columnar or “header plus rows” structure states each key exactly once, then just lists the values. Genuinely less readable to a human glancing at it, genuinely lighter for whatever’s tokenizing it.

Practical Ways to Cut Token Usage

  • Minify before sending. No reason to pay token cost for indentation and line breaks the model doesn’t need to understand the data.
  • Flatten repeated-key arrays into a columns-plus-rows structure when you’re sending a lot of records and don’t specifically need each one to be a self-contained object.
  • Drop fields the model doesn’t actually need for the task at hand. Sending your entire database record when the model only needs three fields out of fifteen is pure waste.
  • Avoid unnecessary nesting. Every extra layer of { } is extra punctuation, and extra punctuation is extra tokens.
  • Shorten key names where you control the schema, though weigh this against your own code’s readability. "active" versus "a" is a real token difference at scale, but it costs you something too if a human has to read that JSON later.

If you’re already reformatting JSON before sending it somewhere, our JSON Formatter and Validator handles the minify step directly in your browser, which covers the easiest win on this list with zero extra tooling.

Frequently Asked Questions

Is there an exact character-to-token ratio I can rely on?

Not a precise one. It varies by language, by how common the specific words and symbols are, and by which model’s tokenizer you’re using. The roughly-4-characters-per-token English average is a reasonable rule of thumb, not a guarantee for any specific payload.

Should I always flatten arrays of objects before sending them to an LLM?

Not always. It’s a real win when you’re sending a lot of uniform records and token cost matters. For smaller payloads, or when you need the model to reason about individual records as self-contained objects, the standard array-of-objects shape is often worth the extra tokens for the clarity it gives the model.

Does minifying JSON change what the model understands from it?

No. Whitespace carries no meaning in JSON, so removing it doesn’t change the data at all, just the token cost of transmitting it.

Are there tools that count tokens exactly for a specific model?

Yes, model providers typically publish or link to their own tokenizer libraries or web-based counters for exactly this purpose. Worth using one directly if you need a precise number for a specific model rather than the general estimate covered here.

Summary

JSON’s structure, specifically the habit of repeating full key names across every object in an array, makes it more token-expensive than it needs to be when you’re sending it to an LLM. We measured character count as an honest stand-in for token count, since we couldn’t run the actual tokenizer in this environment, and even by that conservative measure, flattening repeated keys cut size by two-thirds against pretty-printed JSON. Minify first, flatten when you’re sending a lot of uniform records, and only send the fields the model actually needs.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *