One leaked key, a $55,000 bill
A SaaS founder’s Google Cloud bill normally ran about $200 a month. Then one month it read roughly $55,000. No pricing change. No viral launch. One API key that was never supposed to cost a cent.
The billing report told the whole story: almost the entire spike was Gemini / Generative Language API usage — about 2.2 million requests tied to a single key. And that key wasn’t some secret credential left in a public repo. It was the Firebase-generated client key baked into his iOS app, exactly where Google’s own docs put it.
This isn’t a freak event. It’s a pattern, and it’s getting more common as every app bolts an LLM onto a mobile client. Here’s the mechanism, what to do if it’s already happened to you, and the settings that stop it.
The story is a real r/googlecloud post by the founder it happened to. I’d have commented there directly, but I only came across it a few hours after it went up and the thread was already locked — so here’s the full playbook instead.
How a $200 bill becomes $55,000
Three design facts line up to make this possible:
-
Mobile client API keys are public by design. A Firebase/Google client key isn’t a password — it ships inside your app bundle, and anyone can extract it in minutes. Google’s position is that the key is just an identifier; the security is supposed to come from restrictions you add. Most teams never add them.
-
An unrestricted key can call expensive APIs. By default that public key can reach any API enabled on the project — including the paid Gemini API. Pull the key, point it at the Generative Language endpoint, and you’re spending the project owner’s money at machine speed. 2.2 million requests is trivial for a script to generate.
-
There is no hard spend cap, and the meter reports late. This is the part that turns a scare into a catastrophe. None of the big three give you a true hard ceiling on a normal paid account — a budget “alert” emails you, it doesn’t stop anything. Worse, usage can take hours to show up in billing. In this case the founder got the anomaly email when the visible bill was ~$2k, killed the API and the key within two hours, and still watched it climb to $55k as delayed usage trickled in.
He did everything right after the alert. The architecture had already decided the outcome.
If it’s already happened to you
A first “no” from billing support is not the end. People get these reversed — the key is how you frame it.
- Frame it as fraudulent third-party abuse, not “your usage.” Your strongest facts: Google’s own anomaly system flagged it, and you mitigated within hours. Lead with that.
- Attack the post-mitigation charges hardest. Separate the bill into what accrued before you killed the key versus after. The “after” portion exists only because of Google’s reporting delay — charging you for spend that happened after you’d already shut the door is very hard to defend. Make them account for it line by line.
- Escalate past first-line support. Ask for a billing-adjustment review or supervisor. Use any paid support plan or account team you have. Calm, public pressure helps too — Google engineers read r/googlecloud, and these stories have tipped in the customer’s favor before. (One commenter on the original thread said they were credited in full for a smaller hit.)
- Keep a tight evidence pack: the anomaly email with its timestamp, your mitigation timeline, and the Cloud Monitoring graph showing the request flood from one key UID. The timeline is your case.
- Treat a card chargeback as a last resort. It can get your Google account suspended, which may cost you more than the bill. Weigh it carefully.
The five settings that prevent it
Do these once and the same attack costs the attacker nothing and you nothing.
-
Never call a paid API directly from the client. Proxy Gemini (or any metered API) through your own backend — a Cloud Run service or Cloud Function — so the model key never ships inside the app at all.
-
Put Firebase App Check in front of that backend. App Check (App Attest on iOS, Play Integrity on Android, reCAPTCHA on web) attests that a request came from your genuine, unmodified app. Enforce it on the proxy, and a stolen key from a script gets rejected at the door.
-
Restrict every client key. Add an application restriction (iOS bundle ID / Android SHA-1 / HTTP referrer) and an API restriction that allow-lists only the APIs your app actually uses. A client key should never be able to reach the Generative Language API.
-
Lower the quotas on expensive APIs. Per-minute and per-day request quotas are the closest thing the cloud gives you to a ceiling. Set them to what your real app needs, not the generous default — a 2.2-million-request burst should be impossible by quota alone.
-
Build the kill switch they won’t give you. Wire a budget alert → Pub/Sub → Cloud Function that disables billing on the project when spend crosses a threshold. This is Google’s own documented pattern, and it’s the only thing that actually stops spend rather than just emailing you about it. Without it, you don’t have a cap — you have a notification.
The deeper problem: the cloud has no ceiling
The reason this hurts is the same reason a quiet egress bill hurts — just sped up. The hyperscalers are built so that your bill scales with usage and has no built-in upper bound. Egress is the slow version: a meter that spins a little faster every month as you grow, until one day the transfer line is five figures. Key abuse is the fast version: the same missing ceiling, emptied before you can react.
Both come from the same blind spot — assuming the platform has a sane default that protects you. It doesn’t. The default is “bill keeps running.” Every safeguard above is something you have to turn on.
A 10-minute check
You don’t need an incident to find out where you’re exposed. Pull your project’s API keys and confirm each one has app and API restrictions. Check whether App Check is enforced on anything that reaches a paid API. And confirm you have an automated billing kill switch — not a budget alert, an actual function that disables billing. If you don’t have that last one, you don’t have a cap.
If you’d rather have a second set of eyes, send me your setup and I’ll tell you where the uncapped exposure is — the keys that can reach paid APIs, the missing App Check, the absent kill switch — in a one-page writeup within a business day. And if your guardrails are already solid, I’ll tell you that too.