GEMINI LABJP
FLASH35 — Gemini 3.5 Flash is now GA and powers gemini-flash-latest, making everyday generation faster and more affordableAGENTS — Managed Agents launch in public preview in the Gemini API, running in secure, isolated Google-hosted Linux sandboxesMEDIA — Nano Banana 2 Lite and Gemini Omni Flash bring faster image and high-quality video generation across AI Studio and the APITTS — Streaming speech generation is now supported for gemini-3.1-flash-tts-preview via streamGenerateContentTRANSLATE — A new audio model detects 70+ languages for live speech-to-speech translation while preserving natural intonationSPENDCAP — Project-level spend caps for billing have been added in Google AI Studio to keep costs under controlFLASH35 — Gemini 3.5 Flash is now GA and powers gemini-flash-latest, making everyday generation faster and more affordableAGENTS — Managed Agents launch in public preview in the Gemini API, running in secure, isolated Google-hosted Linux sandboxesMEDIA — Nano Banana 2 Lite and Gemini Omni Flash bring faster image and high-quality video generation across AI Studio and the APITTS — Streaming speech generation is now supported for gemini-3.1-flash-tts-preview via streamGenerateContentTRANSLATE — A new audio model detects 70+ languages for live speech-to-speech translation while preserving natural intonationSPENDCAP — Project-level spend caps for billing have been added in Google AI Studio to keep costs under control
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-api274cost-management8usage-metadataproduction137python102billing4

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-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-07-02
Routing Between Local Gemma 4 and the Gemini API Cut My Bill from ¥32,000 to ¥9,000 — A Production Hybrid Router Design
How I cut a ¥32,000/month Gemini API bill to the ¥9,000 range with hybrid inference: routing design, a full Python router, production pitfalls, and how Gemma 4 arriving on the Gemini API in July 2026 changes the decision.
📚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 →