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-21Advanced

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.

gemini-api277cost-management8usage-metadataproduction140python104billing4

Premium Article

About six months into using the Gemini API in a side project, I opened a Google Cloud bill that was twice what I had budgeted for. Drilling into the console only got me as far as the daily aggregate — there was no way to see which endpoint, which user, or which model had eaten the budget. The real culprit, I realized, was that I had never bothered to record usageMetadata from each response.

usageMetadata is an unassuming field that ships with every Gemini API response. Logging it per request gives you near-perfect attribution of every dollar after the fact. This article shares the exact pattern I now run in production, including how to reconcile your aggregated logs against the actual Google Cloud invoice.

Know every field inside usageMetadata

The first thing that throws people off is how many fields usageMetadata actually has. Print a response from the Python SDK (google-genai) and you'll see this:

from google import genai
 
client = genai.Client(api_key="YOUR_API_KEY")
resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Explain Gemini API cost tracking in 2 sentences.",
)
 
print(resp.usage_metadata)
# UsageMetadata(
#   prompt_token_count=15,
#   candidates_token_count=84,
#   cached_content_token_count=0,
#   thoughts_token_count=0,
#   tool_use_prompt_token_count=0,
#   total_token_count=99
# )

Here's what each field actually represents:

  • prompt_token_count — Total input tokens, including system instructions and chat history
  • cached_content_token_count — Tokens served from Context Caching (typically priced at ~25% of regular input)
  • candidates_token_count — Output tokens generated by the model (sum across all candidates if candidate_count > 1)
  • thoughts_token_count — Thinking tokens emitted by Gemini 2.5 series; billed at the same rate as output tokens
  • tool_use_prompt_token_count — Input tokens consumed internally by Function Calling or Code Execution
  • total_token_count — Sum of the above

The crucial point: you cannot compute the bill from total_token_count alone. The same 1,000 tokens cost wildly different amounts depending on whether they were input, output, cached, or thinking — so you have to log each component separately.

Append every call to a JSONL file

The pattern I run in production is intentionally boring: a thin wrapper around generate_content that appends usageMetadata to a JSONL file before returning. The "save now, aggregate later" mindset is what saves the most time at month-end.

import json
import time
from pathlib import Path
from google import genai
 
LOG_PATH = Path("logs/gemini_usage.jsonl")
LOG_PATH.parent.mkdir(exist_ok=True)
client = genai.Client(api_key="YOUR_API_KEY")
 
def call_gemini_logged(model: str, prompt: str, *, user_id: str, endpoint: str):
    """Wrapper that always writes usageMetadata to JSONL before returning."""
    started = time.time()
    resp = client.models.generate_content(model=model, contents=prompt)
    um = resp.usage_metadata
 
    record = {
        "ts": time.time(),
        "elapsed_ms": int((time.time() - started) * 1000),
        "user_id": user_id,
        "endpoint": endpoint,
        "model": model,
        "prompt": um.prompt_token_count,
        "cached": um.cached_content_token_count or 0,
        "candidates": um.candidates_token_count or 0,
        "thoughts": um.thoughts_token_count or 0,
        "tool_use": um.tool_use_prompt_token_count or 0,
        "total": um.total_token_count,
    }
    with LOG_PATH.open("a", encoding="utf-8") as f:
        f.write(json.dumps(record) + "\n")
 
    return resp
 
# Example
resp = call_gemini_logged(
    model="gemini-2.5-flash",
    prompt="Summarise today's weather in one line",
    user_id="u_001",
    endpoint="/api/weather/summary",
)
print(resp.text)

JSONL is the right choice for two reasons. First, append writes are nearly atomic, so multiple processes can write without corrupting each other. Second, loading the file later into SQLite or DuckDB is a one-liner with read_json_auto().

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
Re-key cost attribution on resp.model_version (the model that actually served the request) so totals stay aligned with the bill even when the default model changes
A nightly audit that raises on price-table-missing models instead of silently falling back to Pro pricing
Track the share of responses served by a model other than the one requested, catching default-model swaps the day they happen
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-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.
📚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 →