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-05-25Advanced

Designing a Semantic Cache for the Gemini API — Embedding-based Answer Caching That Actually Pays for Itself

A practical design for a semantic cache that sits in front of the Gemini API. Combines text-embedding-004, cosine similarity thresholds, versioned cache keys, and TTL design to balance hit rate and answer quality, with Python and Cloudflare Vectorize code that runs in production.

gemini-api277embedding10semantic-cachecost-optimization30vectorize2production140

Premium Article

The Gemini 2.5 Flash pricing is cheap on paper, but when you operate several apps that together pass 50 million downloads, the monthly invoice from the Gemini API starts to bite. In my case, one healing app I run uses Gemini to infer mood tags from a user's daily journal entry. At peak, that endpoint fires dozens of requests per second, and when I broke down the logs I noticed that most of them were variations of the same prompt arriving from different users.

The exact-match cache I had in place wasn't doing much. People write journal entries in their own way: "I think I'm a bit tired today", "Today I'm a little tired", "today I'm sort of tired" — semantically identical, lexically different. An exact-match cache obviously misses all of those. I was paying Gemini every time to return the same three relaxation tags.

After living with that for a while I decided to build a proper semantic cache. This article is the design and lessons I extracted from running it in production. I'll also pull in context from running indie iOS / Android apps since 2014, a cumulative 50M downloads, and the four-site Dolice Labs network, because the "what's worth optimizing for a one-person shop" boundary matters as much as the code.

Why a hash-keyed cache won't save you on LLM responses

A typical HTTP cache uses the URL plus query string as the key. Same URL, same query, same response — that's the contract. LLM responses break that assumption immediately, because the cache key is now a natural-language prompt. Users vary punctuation, politeness level, typos, every parameter you can imagine. The two journal sentences I quoted above hash to completely different keys.

In my healing app the exact-match cache ran at a 6–9% hit rate. Roughly 90% of every 100 requests still hit Gemini directly. That is the classic "the cache is in but doesn't help" pattern. It was tolerable while AdMob revenue absorbed it, but as the share of free-tier users grew, the bill became a real factor.

What you want is a cache that maps semantically close prompts to the same entry. You vectorize the incoming prompt with an embedding model and ask the cache store whether anything close enough already exists.

What semantic caching solves, and what it introduces

A semantic cache absorbs lexical variation, which is the point. It also introduces a new failure mode: prompts that look close in vector space but expect different answers.

"What relaxation methods can I try when I can't sleep?" and "What relaxation music can I try when I can't sleep?" are very close in embedding distance. But the first expects breathing exercises and stretching, the second expects a playlist. Reusing a cached answer across that boundary collapses the answer quality.

So a semantic cache is fundamentally a hit-rate versus quality trade-off. Loose threshold, high hit rate, more wrong reuses. Strict threshold, low hit rate, less benefit. In production I ended up with a threshold that adapts to the length of the user's journal entry. More on that below.

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
You can implement similarity-based cache lookups with Gemini text-embedding-004, with the threshold-tuning pitfalls and a real measurement table you can copy
You get a concrete picture of how a 40–60% hit rate on a 50M-download indie app stack translated into 35% lower monthly API spend, and what trade-offs you accept along the way
You take home three concrete patterns: TTL design, versioned cache keys, and a Cloudflare KV + Vectorize edge deployment, each with the pitfalls I hit during rollout
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-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.
API / SDK2026-06-17
Keep Your Flash-to-Pro Routing Threshold Honest with Shadow Re-evaluation
A Flash-generates, Pro-on-low-confidence router starts drifting the moment you hand-pick its threshold. This is a working build of a loop that samples your kept-Flash outputs, scores them against Pro, and recalibrates the threshold from a quality budget.
API / SDK2026-06-14
Controlling Image Tokens with the Gemini API media_resolution Setting — Tuning Batch Image Classification by Measurement
media_resolution, introduced in the Gemini 3 line, switches how many tokens an image input consumes across three levels. Through real batch-classification measurements, this guide shows how to balance cost and accuracy by assigning the right tier per task.
📚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 →