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
Back to Blog

Using Gemini's Context Caching Without Inflating Your Bill

Gemini APIcontext cachingcost optimizationindie developmentGemini 3 Flash

Have you ever sent the same long preamble to a model over and over and thought, "these token costs are going to add up"? I have. I've been building apps on my own since 2014, watching AdMob revenue and server bills every month, so the "calls × unit price" arithmetic is wired into me. Even a tiny per-token price starts to bite once the call count climbs.

Context caching exists to kill exactly that waste — resending the same context on every request. But there's a trap that the headline on the pricing page doesn't shout about, and if you miss one setting, the thing you added to save money ends up adding a fixed cost instead. Here's how I avoid that, with the actual code.

You're billed for as long as the cache simply sits there

The most important part first. An explicit context cache is billed by the hour just for being stored — whether or not you call it. On the Gemini API, storage runs at roughly $1 per million tokens per hour.

Let's do the math. Suppose you create a cache holding a million tokens, finish your work, and forget to delete it.

  • About $1 per hour
  • About $24 if it sits for a full day
  • About $168 if you don't notice for a week

No inference required. That's the real shape of "I added caching to save money and my bill went up." Lowering your input token cost means nothing if the forgotten storage fee outruns the savings.

I've been burned more than once by "a monthly charge for something I'm not even using" on the ad side of my apps, so I'm reflexively wary of this kind of standing cost. The safe mental model is to treat a cache as one motion that isn't finished until it's deleted.

Below the minimum token count, caching silently does nothing

There's a second behavior that's easy to skim past. Context caching has a minimum token count to become active, and below it the cache is silently skipped.

  • Gemini 2.5 Flash: roughly 1,024 tokens or more
  • Gemini 2.5 Pro: roughly 4,096 tokens or more

So if you cache a short system prompt that doesn't reach the threshold, no cache is created and you pay in full on every call. There's no error, which is why you end up puzzled that "I cached it, but the cost didn't drop." I made exactly that mistake early on, trying to cache a short instruction. In practice, only cache context that reliably clears the threshold — PDFs, manuals, long specs.

The actual code: create, use, and always delete

Here's the basic shape with the new Google Gen AI SDK. The point is to set a short ttl and always run through delete at the end.

from google import genai
from google.genai import types
 
client = genai.Client()  # reads GEMINI_API_KEY from the environment
 
# 1. Cache the long context (a sizable PDF here).
#    Set ttl to 600s so it self-destructs within 10 minutes at most.
cache = client.caches.create(
    model="gemini-2.5-flash",
    config=types.CreateCachedContentConfig(
        contents=[
            types.Part.from_uri(
                file_uri="gs://example-bucket/long-spec.pdf",
                mime_type="application/pdf",
            ),
        ],
        system_instruction="You are an assistant that answers based on this spec.",
        display_name="spec-cache",
        ttl="600s",  # default is 1 hour if unset; shorter means less storage cost
    ),
)
 
# 2. Reference the cache (no need to resend the context each time).
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="List three key points from Chapter 3.",
    config=types.GenerateContentConfig(cached_content=cache.name),
)
print(response.text)
 
# 3. Always delete when done (explicit delete is safer than waiting for ttl).
client.caches.delete(name=cache.name)

With a short ttl, even if your delete call gets skipped by some exception, your storage exposure is capped at "ten minutes' worth." I always keep both safety nets — an explicit delete and a short ttl. I don't leave cleanup entirely to the code, perhaps because my grandfathers, both temple carpenters, drilled into me that a tool is always returned to its place after use.

Catching the ones you forgot: auditing your caches

Even so, forgotten caches happen. It helps to keep a small script that periodically lists your caches and sweeps up the orphans.

from google import genai
 
client = genai.Client()
 
# Show every cache currently alive.
for cache in client.caches.list():
    print(cache.name, cache.display_name, "expires:", cache.expire_time)
    # Delete on the spot once you've judged it unnecessary:
    # client.caches.delete(name=cache.name)

Run this in CI or a daily batch and you'll prevent most "I was paying storage for days without noticing" accidents. Years of always reviewing my monthly app expense statements taught me to put API caches on the same inventory list.

For small contexts, the option of not holding it yourself

The recent Gemini 3 Flash family now ships with implicit caching by default — repeated input gets cached automatically without you managing an explicit cache. When the conditions line up, input token costs drop sharply. And for large, non-urgent workloads, routing to the Batch API trims cost further while loosening rate limits.

The decision rule that emerges is simple. If you reuse a large fixed context many times, use an explicit cache — but pair it with a short ttl and a reliable delete. If the context is small, or you'd rather not own the bookkeeping, lean on implicit caching or Batch. There's no single right answer; it's a choice between the money you want to save and the cleanup effort you're willing to carry.

Start by picking your single longest prompt and counting its tokens. If it clears the threshold, drop it into the code above and run one full "create, use, delete" loop. That alone should change how your monthly invoice looks.