●LOGS — Developer logs for the Interactions API became viewable in the AI Studio dashboard on July 6, so supported calls can be traced after the fact●OMNIFL — gemini-omni-flash-preview generates 3 to 10 second 720p videos through the Interactions API and lets you refine them conversationally●NANOLITE — gemini-3.1-flash-lite-image, known as Nano Banana 2 Lite, reached general availability for ultra-low-latency, cost-effective image generation and editing●COMPUSE — The Computer Use tool entered public preview on Gemini 3.5 Flash with browser, mobile, and desktop support, configurable safety policies, and prompt injection detection●AGENTS — Managed Agents are in public preview, running stateful autonomous agents inside isolated Google-hosted Linux sandboxes●VIDS — Omni is now built directly into Google Vids, bringing higher-quality video generation and text-driven edits to realism, text rendering, and physics●LOGS — Developer logs for the Interactions API became viewable in the AI Studio dashboard on July 6, so supported calls can be traced after the fact●OMNIFL — gemini-omni-flash-preview generates 3 to 10 second 720p videos through the Interactions API and lets you refine them conversationally●NANOLITE — gemini-3.1-flash-lite-image, known as Nano Banana 2 Lite, reached general availability for ultra-low-latency, cost-effective image generation and editing●COMPUSE — The Computer Use tool entered public preview on Gemini 3.5 Flash with browser, mobile, and desktop support, configurable safety policies, and prompt injection detection●AGENTS — Managed Agents are in public preview, running stateful autonomous agents inside isolated Google-hosted Linux sandboxes●VIDS — Omni is now built directly into Google Vids, bringing higher-quality video generation and text-driven edits to realism, text rendering, and physics
A 17% Drop in Output Tokens Sounded Big. Then I Decomposed the Bill
Output pricing fell 16.67% and output tokens fell about 17%. Adding those numbers does not give you the savings. Here is an exact price/volume/cross-term decomposition, plus a loop measurement where the savings rate went down instead of up.
When Gemini 3.6 Flash quietly replaced the Flash model I was calling in the week of July 21, the first thing I did was arithmetic in my head.
Output pricing moving from $9.00 to $7.50 per million tokens. Call it 16.67% off. On top of that, reports of roughly 17% fewer output tokens for the same work.
So, about 33% cheaper.
I rewrote a budget sheet for my automation pipelines on that assumption, and the numbers refused to line up. Some pipelines showed a tenth of the savings I had penciled in.
Two mistakes. I had added two factors that needed to be multiplied. And I had ignored the fact that input pricing did not move at all.
What follows is the decomposition I wrote to clear both up, and the output of actually running it. Prices are the ones published as of July 2026 — they change, so verify against primary sources before you make a call.
When price and volume move together, the order of subtraction matters
Output cost is unit price times quantity. Price goes $9.00 to $7.50, quantity goes 1.00 to 0.83, and the cost lands at 7.50 × 0.83 = 6.225.
That is a 30.84% reduction, not the 33.67% you get by adding.
The missing 2.83 points is the overlap: the discounted price is being applied to the tokens that no longer exist. Double-counted savings.
Economists call this the cross term, and it shows up any time you attribute a change in a product of two factors. It is not small enough to wave away.
from dataclasses import dataclass@dataclass(frozen=True)class Price: name: str inp: float # USD / 1M input tokens out: float # USD / 1M output tokensOLD = Price("gemini-3.5-flash", 1.50, 9.00)NEW = Price("gemini-3.6-flash", 1.50, 7.50)def cost(p: Price, ti: int, to: int) -> float: return (ti / 1_000_000) * p.inp + (to / 1_000_000) * p.outdef decompose(old_p: Price, new_p: Price, to_old: int, to_new: int): """Split the output-side delta into price / volume / cross effects. The three must sum to the observed delta, up to floating point noise.""" q0, q1 = to_old / 1_000_000, to_new / 1_000_000 p0, p1 = old_p.out, new_p.out price_effect = (p1 - p0) * q0 # move price only volume_effect = (q1 - q0) * p0 # move quantity only cross = (p1 - p0) * (q1 - q0) # the part that moved together total = p1 * q1 - p0 * q0 residual = abs((price_effect + volume_effect + cross) - total) if residual > 1e-9: raise AssertionError(f"decomposition does not close: residual {residual}") return price_effect, volume_effect, cross, total
The residual assertion is there because attribution formulas break silently. Adjust one term later and the identity stops holding without any visible symptom.
Three workloads through the same decomposition
I collapsed my own traffic into three shapes — input-heavy, output-heavy, and balanced — and ran them. These are program outputs, not invoice figures.
Workload
Input tokens
Output tokens
Output share of old bill
Total savings
A Classification batch (input-heavy)
2,000,000
40,000
10.7%
3.30%
B Draft generation (output-heavy)
30,000
120,000
96.0%
29.60%
C Conversation (balanced)
200,000
60,000
64.3%
19.82%
Same model, same 17% reduction, and the bill moves anywhere from 3.30% to 29.60%.
Workload A breaks down as $3.3600 before, $3.2490 after. The −$0.11100 delta is a −$0.06000 price effect, a −$0.06120 volume effect, and a +$0.01020 cross term.
Note the sign on that cross term. When price and quantity fall together, the cross term pushes back against the savings. That is precisely why the additive estimate always errs on the optimistic side.
And A stalls for a structural reason: output is only 10.7% of the bill. The other 89.3% is input at an unchanged $1.50. Most of the spend sits where the discount never lands.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Adding 16.67% and 17% gives 33.67%; composing them correctly gives 30.84% — the 2.83-point gap traced to the cross term with runnable code
✦The same 17% reduction produces 3.30% savings on an input-heavy classification batch and 29.60% on output-heavy drafting
✦A 12-turn agent loop where savings fell from 20.40% to 15.59%, and history pruning that beat the model switch at 26.9%
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Generalized, the total savings rate is bounded by:
total savings = output share × ( 1 − new output price / old output price × ( 1 − token reduction ) )
The bracketed term is the output-side savings — 30.84% at a 17% reduction. Multiply by your output share and you have your ceiling.
Output share
0% reduction
8% reduction
17% reduction
25% reduction
10%
1.67%
2.33%
3.08%
3.75%
30%
5.00%
7.00%
9.25%
11.25%
50%
8.33%
11.67%
15.42%
18.75%
70%
11.67%
16.33%
21.58%
26.25%
90%
15.00%
21.00%
27.75%
33.75%
The column that settled the question for me was the leftmost one.
Even if your output token count does not fall by a single token, the price change alone takes 16.67% off the output side. Whether the reported 17% reproduces on your traffic does not decide whether to switch. You do not lose either way.
Running it backwards: at a 20% output share, you need a 10.0% token reduction just to clear 5% total savings. Above a 30% share, you clear 5% with zero reduction.
Which reorders the work. The first thing to measure is not the reduction rate. It is what fraction of your bill is output in the first place.
The part I got backwards: longer loops save less
I expected agent loops to amplify the benefit. Each turn's output becomes the next turn's input, so a 17% smaller output should drag input tokens down with it across the run.
The first half of that was right. The second half was not.
Conditions: 4,000 tokens of system instruction, 600 tokens of user input, and 900 tokens of tool output appended every turn, with 1,500 output tokens per turn.
IN_P, OUT_OLD, OUT_NEW = 1.50, 9.00, 7.50SYS, USER, OBS = 4_000, 600, 900 # appended every turndef run(turns: int, out_per_turn: int, reduction: float = 0.0, prune: bool = False, keep: int = 3): """Accumulate cost including model output re-billed as input on later turns.""" out_tokens = out_per_turn * (1 - reduction) out_price = OUT_NEW if reduction else OUT_OLD history, ti_total, to_total = [], 0, 0 for _ in range(turns): ctx = history[-keep * 2:] if prune else history ti = SYS + USER + sum(ctx) ti_total += ti to_total += out_tokens history += [out_tokens, OBS] cost = ti_total / 1e6 * IN_P + to_total / 1e6 * out_price return ti_total, to_total, costfor t in (1, 3, 5, 8, 12): _, _, old_cost = run(t, 1500) _, _, new_cost = run(t, 1500, reduction=0.17) print(f"{t:2d} turns: {(1 - new_cost / old_cost) * 100:5.2f}% saved")
Turns
Old (3.5 Flash)
New (3.6 Flash)
Total savings
Input tokens saved
1
$0.02040
$0.01624
20.40%
0.00%
3
$0.07200
$0.05837
18.94%
3.64%
5
$0.13800
$0.11336
17.85%
5.43%
8
$0.26400
$0.21999
16.67%
6.87%
12
$0.48240
$0.40720
15.59%
7.88%
The propagation is real — input tokens do fall 7.88% by turn 12.
The savings rate still slides from 20.40% to 15.59%.
Share of spend explains it. Each turn inflates the history, and the input side — where no discount applies — takes a larger slice of the bill. Dilution outruns propagation.
Longer loops get less out of the switch, not more. Short single-shot calls benefit most, which is the opposite of what I assumed going in.
Pruning history beat switching models
Same 12 turns, with history trimmed to the last three exchanges.
Condition
At old pricing
After switching
Savings from the switch
Full history
$0.48240
$0.40720
15.59%
Last 3 exchanges
$0.35280
$0.29137
17.41%
Pruning alone took 26.9% off — more than the 15.59% from the model switch.
The two do not compete. Prune first and the switch itself recovers from 15.59% to 17.41%, because less input dilution means a larger share of spend sits where the discount applies.
I had the order wrong. A price drop arrives and the instinct is to evaluate migration. Reviewing your own context strategy first turned out to be worth more in dollars — and it made the price drop worth more too.
Reconciling the model against real invoices
Everything above is a projection. It only matters once it meets your actual usage.
Gemini API responses carry usage_metadata. Character-count estimates drift badly on mixed-script text, so record the reported values instead of estimating.
import json, time, pathlibLEDGER = pathlib.Path("usage_ledger.jsonl")def record_usage(response, *, model: str, route: str) -> dict | None: """Append one call's measured tokens. Never let bookkeeping break the request.""" usage = getattr(response, "usage_metadata", None) if usage is None: # some streaming paths only attach it to the final chunk print(f"[warn] no usage_metadata route={route} model={model}") return None row = { "ts": time.time(), "model": model, "route": route, # the key that makes share-of-spend meaningful "prompt": getattr(usage, "prompt_token_count", 0), "cached": getattr(usage, "cached_content_token_count", 0) or 0, "output": getattr(usage, "candidates_token_count", 0), "thoughts": getattr(usage, "thoughts_token_count", 0) or 0, } try: with LEDGER.open("a", encoding="utf-8") as f: f.write(json.dumps(row, ensure_ascii=False) + "\n") except OSError as e: print(f"[warn] ledger append failed: {e}") return row
Tagging every row with route is the part I learned the hard way in production. Grouping by model gives you nothing usable: input-heavy and output-heavy pipelines share a model, and the blended share matches none of them.
The other trap was thoughts_token_count. Thinking tokens bill as output but are not included in candidates_token_count. Record them separately or your output share will read lower than it is.
Now you have per-route shares to drop into the table above and estimate the savings before you touch anything.
The order I would recommend
Three steps, in this sequence:
Log usage_metadata per route for one to two weeks and compute the output share of spend
For any route below a 30% output share, postpone the model switch and fix how context accumulates
Switch the high-share routes first, and reconcile before and after through the same decomposition
Step 3 matters because if you change prompts while switching models, you lose the ability to say which effect produced the drop. Price effect is not yours to move. Volume effect is the only one your design controls. Keeping them separate means the bill itself tells you what to do next.
A 17% drop in output tokens was good news. Where that 17% lands on your invoice, though, is something only your own ledger can answer.
As an indie developer, I rebuilt a budget sheet twice before this clicked. If it saves you the second pass, that is enough.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.