DEV Community

Integration Yard
Integration Yard

Posted on Originally published at apify.integrationyard.com

Compact, Lossless JSON Output for LLM Agents: How Much Do You Actually Save?

TL;DR

  • We measured 882 real rows scraped by five of our Apify actors in three encodings: pretty JSON, minified JSON and our lossless compact output.
  • With OpenAI's o200k_base tokenizer (GPT-4o / GPT-5 family), compact output used 33.8% fewer tokens than pretty JSON and 18.3% fewer than minified JSON.
  • Savings per actor ranged from 43% (AliExpress) down to 13% (WeChat Articles). Short, repetitive rows win big; long free-text rows barely move.
  • Every file passed a lossless round-trip check. No rows or fields dropped.
  • At 300,000 rows a month on GPT-5.4, that is about $171 vs $113 in input tokens, or roughly $58 a month saved for changing one input flag.

If you've ever pasted a scraper export into ChatGPT or sent one to an agent as a tool result, you've paid for the same key names hundreds of times. "title", "likeCount", "authorName", "scrapedAt": in an array of JSON objects every row repeats every key, and the model bills you for each copy before it reads a single value.

That cost is easy to ignore on one prompt and hard to ignore in production. An agent that pulls a few thousand rows a day, re-reads them on every turn, and runs on a frontier model can spend a real share of its budget on punctuation and repeated field names.

This post is the full benchmark behind the outputFormat: "compact" option in our Bilibili, Weibo, WeChat Articles, AliExpress and Douban actors on Apify. It covers what the format is, how we measured it, the per-actor results (including the ones that don't look good), what that means in dollars at current prices, when you should not use it, and how to plug it into LangChain or a plain LLM call.

Disclosure: we build and maintain these actors.

Contents

Why tokens cost money

LLM APIs bill per token, and input tokens are usually most of the bill for data-heavy work. Your prompt holds the data, and the answer is short. Here are list prices per million input tokens as of October 2026:

Model Input $ / 1M tokens Output $ / 1M tokens Source
OpenAI GPT-5.4 $2.50 $15.00 OpenAI pricing
OpenAI GPT-5 $1.25 $10.00 OpenAI pricing
OpenAI GPT-5 mini $0.25 $2.00 OpenAI pricing
Anthropic Claude Sonnet 5.5 $2.00 $10.00 Anthropic pricing

A token is not a character or a word. Tokenizers split text into chunks they've learned are common. That means JSON's structure isn't free: quotes, colons, commas, braces, the newlines and indentation in pretty-printed JSON, and above all the repeated key names all become tokens.

Here's a quick way to see it. In our benchmark, one pretty-printed row averaged 228 tokens with the o200k_base tokenizer. The same row averaged 185 tokens minified and 151 tokens in compact form. The data didn't change. Only the packaging did.

Three things make this worse in agent workloads than in one-off prompts:

  1. Re-reading. An agent loop sends the whole conversation, tool results included, back to the model on each turn. A 40,000-token tool result read five times costs 200,000 input tokens.
  2. Context limits. Even with very large context windows, fewer tokens per row means more rows fit before you hit the limit or have to chunk.
  3. Latency. Prompt processing time grows with input length, so a smaller prompt also comes back faster.

Prompt caching and batch discounts help too, and they stack with what follows. Fewer tokens is still fewer tokens, whether they're billed at the full or the cached rate.

The format: what changes, and what doesn't

A normal scraper export is an array of JSON objects, and every row repeats every key name:

[
  {"id": "BV1xx", "title": "2026 keyboard roundup", "views": 12034},
  {"id": "BV2yy", "views": 877}
]
Enter fullscreen mode Exit fullscreen mode

Compact output is our token-optimized encoding of exactly the same rows. It removes the structural repetition that LLMs bill you for, and keeps everything that matters:

  • Lossless. Every value, type and null survives, and so does the difference between a field that's null and a field that's absent. Plenty of "compact" formats quietly lose that last one.
  • Still JSON. Any JSON parser reads it. No new parser, no new library.
  • Robust on messy, real data. Mixed row shapes, Chinese text, nested lists and unusual characters are all handled. We verify the round trip on every format change.
  • Additive. Your normal dataset is untouched. The compact copy is an extra record on the same run, at no extra cost.

Getting all of that right at once is harder than it looks, which is why we built it into the actors rather than leaving it as an exercise.

Methodology

We wanted numbers we'd be comfortable defending, so the benchmark is deliberately boring:

  • Real data, not synthetic. 882 rows exported from real runs of five actors: AliExpress (130 rows), Bilibili (406), Douban (150), Weibo (119) and WeChat Articles (77). They're mostly flat rows (a few fields hold small lists), and some exports mix row shapes, such as Bilibili comments next to replies and Weibo posts next to comments, because that's what real exports look like.
  • Three encodings of the exact same rows. Pretty JSON (json.dumps(rows, indent=2, ensure_ascii=False)), minified JSON (separators=(",", ":")), and compact output, exactly as the actor writes it to the OUTPUT_COMPACT key.
  • A lossless check first. Each file had to round-trip back to the exact original rows before we measured it. A format that drops data can always look smaller, so this check comes first.
  • Two real tokenizers. tiktoken with o200k_base (GPT-4o, GPT-4.1 and GPT-5 family) and cl100k_base (GPT-4 / GPT-3.5). The headline numbers use o200k_base. cl100k_base came out within about two points (31.7% vs pretty, 17.1% vs minified).
  • Two baselines. Many "X% smaller than JSON" claims compare against pretty JSON, which is the easy baseline. We report both, and we think minified JSON is the honest one, because it's what a careful pipeline already sends.

What we did not measure: downstream answer accuracy, and non-OpenAI tokenizers such as Claude's, Llama's or Qwen's. Independent TOON benchmarks show savings vary by tokenizer, so measure on the model you actually use.

Horizontal bar chart of total tokens for the same 882 rows: pretty JSON 201,384, minified JSON 163,131, compact output 133,220

Results by actor

Bar chart of token savings per actor. Versus pretty JSON: AliExpress 43.1%, Bilibili 41.9%, Douban 39.6%, Weibo 24.8%, WeChat Articles 12.8%. Versus minified JSON: 25.6%, 25.9%, 22.3%, 4.9%, 6.2%

Actor Rows Pretty Minified Compact vs pretty vs minified
AliExpress Scraper 130 42,425 32,461 24,154 43.1% 25.6%
Bilibili Scraper 406 67,672 53,034 39,320 41.9% 25.9%
Douban Scraper 150 26,632 20,696 16,091 39.6% 22.3%
Weibo Scraper 119 22,611 17,879 17,010 24.8% 4.9%
WeChat Articles Scraper 77 42,044 39,061 36,645 12.8% 6.2%
All five 882 201,384 163,131 133,220 33.8% 18.3%

Token counts with o200k_base.

Three patterns explain the spread:

1. Short values plus many fields means big wins (AliExpress, Bilibili, Douban: 40–43%). Product listings, video comments and book or film entries have lots of short fields: IDs, prices, counts, timestamps, short titles. Structure is a large share of each row, so removing the repetition removes a large share of the tokens. Bilibili gets an extra boost from row count: the more rows, the bigger the saving.

2. Mixed row shapes cut into the gain (Weibo: 25% vs pretty, under 5% vs minified). A Weibo export mixes 22-key posts with 10-key comments. Rows of very different shapes share less structure, so there's less repetition to remove. If you filter to one row type, you'd recover most of the gap.

3. Long free text leaves little to remove (WeChat Articles: 13%). WeChat rows are mostly article titles and summaries in Chinese, so the values already dominate the token count. Removing repetition still helps, but there's not much structure left to strip.

Bytes tell the same story, with slightly larger gains: 40–46% fewer UTF-8 bytes for AliExpress, Bilibili and Douban against pretty JSON. That matters if you store or move exports at volume.

What it saves in dollars

Grouped bar chart of monthly input cost for 300,000 rows: GPT-5.4 $171 pretty, $139 minified, $113 compact; GPT-5 $86, $69, $57; GPT-5 mini $17, $14, $11

Here's a worked example using the measured per-row averages: 228.3 tokens pretty, 185.0 minified and 151.0 compact.

Say an agent ingests 10,000 scraped rows a day (about 300,000 a month) and reads each row once:

Pretty JSON Minified JSON Compact
Input tokens / month 68.5M 55.5M 45.3M
GPT-5.4 at $2.50 / 1M $171.24 $138.72 $113.28
GPT-5 at $1.25 / 1M $85.62 $69.36 $56.64
GPT-5 mini at $0.25 / 1M $17.12 $13.87 $11.33

On GPT-5.4, switching from pretty JSON saves about $58 a month (~$696 a year). Switching from minified saves about $25 a month. If the agent re-reads the data across several turns, multiply both by the number of reads. On a mini model the dollar savings are small, but you can fit about a third more rows into the same context.

These are input costs only, at OpenAI list prices from October 2026, and they use the OpenAI tokenizer we measured. Claude uses a different tokenizer, so check your own counts before applying these percentages to Claude prices.

How this compares to TOON, CSV and lossy compressors

Token-efficient formats are a crowded topic right now, so it's worth placing this one.

  • TOON (Token-Oriented Object Notation) is the best-known "JSON for LLMs" format. It uses a YAML-like layout with tabular arrays. Independent benchmarks report about 34% fewer tokens than minified JSON on uniform tabular data, but 14–17% *more* on nested or semi-uniform data, and smaller savings on non-OpenAI tokenizers. The author also notes that the widely quoted "40–60%" figure is measured against pretty JSON. Our numbers sit in the same range for the same reason. Tabular data compresses well, and mixed shapes don't.
  • CSV is very compact for flat tables, but it has no types (is "123" a string or a number?), no null, and no way to say "this key was absent". The TOON project's own benchmarks exclude CSV from parts of their accuracy tests because it can't represent the non-flat datasets.
  • Lossy "AI compressors" can post bigger headline numbers by summarizing or sampling. We tested one (headroom) on the same kind of files, and it dropped rows or fields on 7 of 8 sample datasets. That's fine for an agent's scratch memory, but not for data a customer is paying to receive intact.
  • Agent-loop caveat. A 2026 study of token-optimized formats in agentic systems found that formats which shrink tool outputs the model must write can increase total cost when parsing failures trigger extra turns. Compact output is an input format: the model reads it and never has to produce it.

We built compact output to be plain JSON that keeps types and null and round-trips exactly, so it's safe for data a customer is paying for.

When not to use it

Compact output isn't always the right call. Skip it when:

  • You're loading a spreadsheet, BI tool or database. Use the normal dataset export in CSV, Excel or JSON. The compact format exists to cut LLM tokens, not to replace ordinary exports.
  • Your rows are mostly long text. As the WeChat result shows, savings drop toward 10%, and per-row objects can be easier to debug.
  • You're mixing very different row types in one table. Weibo's posts plus comments showed this. Either filter to one row type, or accept the smaller gain.
  • You need row-level retrieval. For RAG, you usually embed one row or chunk at a time. Each chunk is small, so the saving shrinks. Compact output pays off when you send many rows together in one prompt.
  • The model has to produce this format. Keep model outputs in ordinary JSON or structured outputs. Use compact for what the model reads.
  • You already minify, and your volume is tiny. At a few hundred rows a month, an 18% saving on minified JSON is worth less than your time.

Using it with LangChain and LLM pipelines

Set "outputFormat": "compact" on the run. The dataset is unchanged and so is the price. The actor also writes the compact copy to the run's key-value store under OUTPUT_COMPACT.

1. Fetch it with the Apify client

import json
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dheerajitbhu/aliexpress-scraper").call(run_input={
    "mode": "search",
    "searchQueries": ["mechanical keyboard"],
    "maxResults": 100,
    "outputFormat": "compact",
})
record = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT_COMPACT")
payload = record["value"]  # compact copy of the run's rows
Enter fullscreen mode Exit fullscreen mode

(Field names in run_input vary by actor. Check each actor's input tab.)

2. Send it straight to the model

from openai import OpenAI

prompt = (
    "Below are AliExpress product rows in compact JSON.\n\n"
    + json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
    + "\n\nWhich five products have the best rating-to-price ratio?"
)
resp = OpenAI().chat.completions.create(
    model="gpt-5", messages=[{"role": "user", "content": prompt}])
print(resp.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

3. Use it as an agent tool result in LangChain

from langchain_core.tools import tool

@tool
def latest_products(query: str) -> str:
    """Return current AliExpress listings for a query in compact JSON."""
    run = client.actor("dheerajitbhu/aliexpress-scraper").call(run_input={
        "mode": "search", "searchQueries": [query], "maxResults": 50, "outputFormat": "compact"})
    rec = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT_COMPACT")
    return json.dumps(rec["value"], ensure_ascii=False, separators=(",", ":"))
Enter fullscreen mode Exit fullscreen mode

Because tool results are re-sent on every later turn of the agent loop, the savings repeat every time the agent reads that result again.

4. Need regular dicts? For pandas, LangChain Documents or a vector store, read the normal dataset from the same run. It's always there, unchanged:

from langchain_core.documents import Document
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
docs = [Document(page_content=r.get("title", ""), metadata=r) for r in rows]
Enter fullscreen mode Exit fullscreen mode

FAQ

Is it really lossless?
Yes, within what JSON can represent. Every benchmark file round-tripped back to the exact original rows, including real nulls and keys absent from some rows.

Does compact output change what I pay for the actor?
No. The run pushes the same rows to the dataset at the same per-result price. OUTPUT_COMPACT is an extra key-value store record.

Do LLMs understand the compact shape?
In our use, yes, It reads like a table. We haven't run a formal accuracy benchmark, so test your own task if accuracy is critical.

Why not just use CSV?
CSV loses types and can't tell null from an absent key. Compact output keeps both and still parses with json.loads.

Does this work with Claude, Gemini or open models?
The format works with any model. The percentages above come from OpenAI tokenizers, and other tokenizers will give different numbers. The structural saving, writing keys once instead of N times, applies everywhere.

Why are your numbers smaller than some format headlines?
Big headline numbers usually come from comparing against pretty-printed JSON on perfectly uniform synthetic data. On 882 real, messy rows we measured 13–43% per actor against pretty JSON and 18% overall against minified. We'd rather quote the smaller, real number.

Try it

Compact output is available on five Integration Yard actors on Apify. Add "outputFormat": "compact" to any run:

Apify's free plan includes monthly platform credit, so you can run it on your own query and count the tokens yourself. If your numbers differ from ours, email apify@integrationyard.com. We'll publish corrections.

Benchmark data: 882 rows, tiktoken o200k_base and cl100k_base, October 2026. Prices are list prices checked October 20, 2026.

Top comments (0)