◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — The old Deep Research agent shut down on 10/23. If calls fail, move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — The old Deep Research agent shut down on 10/23. If calls fail, move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-06-01Advanced

Measuring the Economics of Each Gemini-Powered Feature — So You Can Keep It, Fix It, or Retire It

Gemini API costs are visible at the account level, but the profitability of an individual feature never shows up on its own. This guide shows how to tag every request, build a per-feature cost ledger, join it with revenue signals from AdMob and in-app purchases to compute contribution margin, and decide whether to keep, fix, or retire each feature — with the code I actually run.

gemini-api290unit-economicscost-management8production140AdMob9decision-making

✦ Premium Article

Halfway through one month, I noticed that a small "auto-caption" feature in one of my wallpaper apps was costing more in Gemini API calls than it was earning in ad revenue. The app as a whole was profitable, so this loss would never have surfaced from staring at the monthly invoice. Account-level cost is visible in the Google Cloud console, but "which feature earns what, and spends what" lives nowhere unless you deliberately design for it.

I have built apps independently since 2014, running a portfolio with over 50 million cumulative downloads on AdMob and in-app purchases. But once I began wiring Gemini into production, this "I can't see per-feature economics" problem suddenly got heavy. Traditional features cost essentially nothing to run after you write them, whereas a feature that calls Gemini bills you every time a user touches it. For the first time, I needed each feature to carry its own profit-and-loss statement.

Here I want to share the design that takes you from per-request cost recording to per-feature contribution margin, and finally to a keep / fix / retire decision. I covered the recording side in an implementation pattern for tracking production cost with usageMetadata, so this article focuses on the layer above it: turning recorded cost into a decision.

Why per-feature economics stays invisible if you leave it alone

Because cost and revenue are recorded in different places, at different granularities. Cost accumulates in Google Cloud billing and usageMetadata by model and by account. Revenue accumulates in your AdMob dashboard by ad unit, and purchases land in the App Store or Google Play by product. None of these three carries a "feature" axis.

My first mistake was to attack cost alone. I shortened prompts, shifted to Flash-Lite, leaned on caching — and cost did drop by about 20%. But none of that answered the real question: should this feature have existed at all? Cost optimization is not a substitute for a profitability decision. Profitability only means something when it sits next to revenue.

So I set the starting point of the whole design as one rule: every Gemini request must carry a tag saying which feature it is for. It is unglamorous, but without it none of the downstream aggregation is possible.

Step 1: Build a cost ledger tagged by feature

Funnel every request through a single function, and force the caller to pass a feature. The code below runs as-is (Python, the google-genai SDK).

import time
import sqlite3
from google import genai
 
client = genai.Client()  # reads GEMINI_API_KEY from the environment
 
# Approximate Flash unit prices (USD per 1M tokens). Confirm actual prices on the pricing page.
PRICE_PER_1M = {
    "gemini-2.5-flash": {"input": 0.30, "output": 2.50},
    "gemini-2.5-flash-lite": {"input": 0.10, "output": 0.40},
}
 
def _cost_usd(model: str, usage) -> float:
    p = PRICE_PER_1M.get(model)
    if not p:
        return 0.0
    in_tok = usage.prompt_token_count or 0
    out_tok = (usage.candidates_token_count or 0) + (usage.thoughts_token_count or 0)
    return in_tok / 1_000_000 * p["input"] + out_tok / 1_000_000 * p["output"]
 
def generate_tracked(feature: str, model: str, contents, db: sqlite3.Connection):
    """feature tag is mandatory. Record one ledger row, then return the response."""
    t0 = time.time()
    resp = client.models.generate_content(model=model, contents=contents)
    u = resp.usage_metadata
    db.execute(
        "INSERT INTO ai_cost_ledger (ts, feature, model, in_tok, out_tok, cost_usd, latency_ms) "
        "VALUES (?,?,?,?,?,?,?)",
        (
            int(t0), feature, model,
            u.prompt_token_count or 0,
            (u.candidates_token_count or 0) + (u.thoughts_token_count or 0),
            _cost_usd(model, u),
            int((time.time() - t0) * 1000),
        ),
    )
    db.commit()
    return resp

The ledger schema is just this.

CREATE TABLE ai_cost_ledger (
  id        INTEGER PRIMARY KEY,
  ts        INTEGER NOT NULL,   -- UNIX seconds
  feature   TEXT NOT NULL,      -- e.g. "caption_gen", "wallpaper_tagging"
  model     TEXT NOT NULL,
  in_tok    INTEGER NOT NULL,
  out_tok   INTEGER NOT NULL,
  cost_usd  REAL NOT NULL,
  latency_ms INTEGER NOT NULL
);
CREATE INDEX idx_ledger_feature_ts ON ai_cost_ledger (feature, ts);

The key is to take feature as a required argument with no default value. If you allow a default, an untagged request will slip in whenever you are in a hurry, and six months later you will see the one result you least want to see: "unknown is 30% of all cost." I did exactly that once. Forcing the tag is a small kindness to your future self.

✦

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
✦A runnable recording layer that tags every request so cost is captured per feature, not just per account
✦The SQL that joins cost with AdMob, in-app purchase, and engagement revenue signals to compute each feature's contribution margin — and how to read it
✦A keep / fix / retire decision matrix built on contribution margin and improvement headroom, plus the real numbers from the one feature I actually retired
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.

or
Unlock all articles with Membership →
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.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ API / SDK2026-07-15
Sample what you already accepted — an audit budget that catches silent quality drift
A confidence gate only ever looks at output the model hesitated on. Silent drift sinks into the batch that sailed through. Working from a fixed 30-minutes-a-day review budget, this walks through deriving detection time from the binomial, reallocating the same budget across risk strata, and catching slow decay with a cumulative monitor.
◈ API / SDK2026-07-04
When Gemini API Leaks Japanese Into Your English Output Once in a While — Field Notes on Measuring the Contamination Rate and Tightening It in Stages
You told Gemini to answer in English, and 3 out of 100 runs slip a Japanese sentence into the tail. Here is why you cannot stop that 'once in a while', and a production pattern that measures the contamination rate as an SLO and tightens it with graded recovery, with working code.
◈ API / SDK2026-05-20
Evolving Gemini API Structured Output Schemas in Production — Design Notes from an Indie App
How I rebuilt the JSON contract layer for a Gemini-powered recommendation feature in a long-running indie app — Dual-Emit, Sunset protocol, and a Python compatibility checker.