GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
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-api277unit-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-06-28
A Promotion Gate So gemini-flash-latest Flipping to 3.5 Flash Doesn't Break Your Pipeline at 3 AM
Floating aliases like gemini-flash-latest swap their target on every GA, quietly shifting the assumptions your unattended automation depends on. Here is a role-to-pinned-ID indirection layer, an acceptance harness that measures four metrics against your own golden set, and threshold-driven promotion and automatic rollback — with working code.
API / SDK2026-06-21
Track Gemini API Costs in Production with usageMetadata — A Per-Request Logging Pattern That Reconciles With Your Bill
A production pattern for capturing Gemini API's usageMetadata per request to attribute spend by endpoint, user, and model — hardened for the 3.5 Flash GA era where the default model can shift under you. Covers pricing keyed on resp.model_version and a nightly audit that flags model drift and unknown models before the invoice does.
API / SDK2026-06-19
Your Managed Agents Bill Has a Second Axis: Drawing a Budget Boundary Around Sandbox Runtime
Managed Agents in public preview bills for tokens and for how long its Google-hosted sandbox stays alive. A single hung run quietly drains your budget on that second axis. Here is a working Python design for wall-clock caps, idle teardown, and a concurrency ceiling.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →