# Unexpected €54k billing spike in 13 hours: Firebase browser key without API restrictions used for Gemini requests

**URL:** <https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262>\
**Category:** Gemini API\
**Tags:** gemini-api, gemini, billing\
**Created:** [April 15, 2026, 12:35pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262 "2026-04-15T12:35:44Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![zanbezi](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/zanbezi/32/47598_2.png) [@zanbezi](https://discuss.ai.google.dev/u/zanbezi)\
**Post date:** [April 15, 2026, 12:35pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/1 "2026-04-15T12:35:44Z")

</div>

Hello,

We are looking for guidance regarding an unexpected €54,000+ Gemini API charge that occurred within a few hours after enabling Firebase AI Logic on an existing Firebase project.

**Background:**

We created the project over a year ago and initially used it only for Firebase Authentication. Recently, we added a simple AI feature (generating a web snippet from a text prompt) and enabled Firebase AI Logic.

**What happened:**

Shortly after enabling this, we experienced a sudden and extreme spike in Gemini API usage. The traffic was not correlated with our actual users and appeared to be automated. The activity occurred within a short overnight window and stopped once we disabled the API and rotated credentials.

**Additional observations:**

- We had a budget alert (€80) and a cost anomaly alert, both of which triggered with a delay of a few hours
- By the time we reacted, costs were already around €28,000
- The final amount settled at €54,000+ due to delayed cost reporting

This describes our issue in more detail:

> **[Google API Keys Weren't Secrets. But then Gemini Changed the Rules. ◆ Truffle...](https://trufflesecurity.com/blog/google-api-keys-werent-secrets-but-then-gemini-changed-the-rules)**
>
> Google spent over a decade telling developers that Google API keys (like those used in Maps, Firebase, etc.) are not secrets. But that's no longer true.

**Aftermath** :

We worked with Google Cloud support and provided logs and analysis. The charges were classified as valid usage because they originated from our project, and our request for a billing adjustment was ultimately denied.

This usage was clearly anomalous, not user-driven, and does not reflect intended or meaningful consumption of the service.

**Questions:**

- Has anyone encountered a similar issue after enabling Firebase AI Logic or Gemini?
- Are there recommended safeguards beyond App Check, quotas, and moving calls server-side?
- Is there any escalation path we may have missed for cases like this?

Any guidance or shared experience would be greatly appreciated.

---

<div class="post-metadata">

**Author:** ![Logan\_Kilpatrick](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/logan_kilpatrick/32/129_2.png) [@Logan\_Kilpatrick](https://discuss.ai.google.dev/u/Logan_Kilpatrick)\
**Post date:** [April 16, 2026, 2:12pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/3 "2026-04-16T14:12:10Z")

</div>

Hey @zanbezi ! Sorry to hear about this. A few things:

1. We have billing account caps rolled out to users of the Gemini API, see: [https://ai.google.dev/gemini-api/docs/billing#tier-spend-caps](https://ai.google.dev/gemini-api/docs/billing#tier-spend-caps), tier 1 users can spend $250 a month and then are cut off by default (there is a 10 minute delay in all of the reporting)

2. We now support project spend caps, if you want to set a customer spend cap, you can also do that (I have my account set at $50 so I don’t spend too much accidenlty when building, the same 10 minute delay applies here too): [https://ai.google.dev/gemini-api/docs/billing#project-spend-caps](https://ai.google.dev/gemini-api/docs/billing#project-spend-caps)

3. We are moving to disable the usage of unrestricted API keys in the Gemini API, should have more updates there soon.

4. We now generate Auth keys by default for new users (more secure key which didn’t exist when the Gemini API was originally created a few years ago) and will have more to share there soon.

5. You should generally avoid putting a key in client side code as if it is exposed, even with the restrictions above you can incur costs.

6. In many cases, we can automatically detect when a key is visible on the public web and shut down those keys automatically for security reasons (this happened to me personally, I accidentally pushed my API key to the public API docs and it was shut down in minutes).

7. By default, keys generated in Google AI Studio are restricted to just the Gemini API, no other services are enabled. However keys generated from other parts of Google Cloud have this cross service capability, you can double check keys and make sure they are restricted for just the resource you need.

8. Pls email me and our team can take a look into this case ([Lkilpatrick@google.com](mailto:Lkilpatrick@google.com)), we take this all very serious and have been pushing hard to land all the features mentioned above and more.

9. We just started the prepaid billing rollout which means you have to pay ahead of time to use the Gemini API, this is rolled out to all new US billing accounts as of yesterday and rolling out globally right now. This is yet another way to give developers more control over their spending / costs and ensure you know what you are signing up for when using the Gemini API.

I hope this helps and sorry for the hassle on this experience, pls email me if there is more to chat about!

---

<div class="post-metadata">

**Author:** ![zanbezi](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/zanbezi/32/47598_2.png) [@zanbezi](https://discuss.ai.google.dev/u/zanbezi)\
**Post date:** [April 16, 2026, 2:35pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/4 "2026-04-16T14:35:02Z")

</div>

Hi,

Thanks for the detailed response, we really appreciate it. It is good to see that additional safeguards (like spend caps) are being introduced.

I will reach out via email with the details so your team can take a closer look.

Thanks again for taking the time to respond.

---

<div class="post-metadata">

**Author:** ![Wenzel](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/wenzel/32/115759_2.png) [@Wenzel](https://discuss.ai.google.dev/u/Wenzel)\
**Post date:** [April 16, 2026, 2:35pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/5 "2026-04-16T14:35:43Z")

</div>

Great to see you here Logan. This is the proper way to deal with a fiasco like this one.

---

<div class="post-metadata">

**Author:** ![Balinor](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/balinor/32/30427_2.png) [@Balinor](https://discuss.ai.google.dev/u/Balinor)\
**Post date:** [April 19, 2026, 9:20am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/7 "2026-04-19T09:20:46Z")

</div>

Good afternoon, I’m glad you’ve started showing signs of activity on the forum again. But you owe us an answer to a pressing question about content blocking - the introduction of a stricter safety filter (which isn’t working correctly, ruining both basic, innocent interactions and creative texts and stories). You asked us to provide examples, and we did, so how long do we have to wait for an official response? Could you please stop running away from us and answer honestly?

> [@Did Google AI Studio silently change safety filtering today? Full responses now get erased instead of stopping generation – this breaks creative writing and RP](https://discuss.ai.google.dev/t/did-google-ai-studio-silently-change-safety-filtering-today-full-responses-now-get-erased-instead-of-stopping-generation-this-breaks-creative-writing-and-rp/130716/9):
>
> Hey folks, I have not seen this change, does anyone have a screen recording of the behavior? Generally, AI Studio is not a creative writing platform. The playground is meant as a place to quickly test Google’s models as a developer, then get an API key and go build something with it. I think for developer use cases, you can use the API how you were initially describing, but not sure about this safety change, in general it seems to be working as intended but happy to see the screenshot / recordin…

---

<div class="post-metadata">

**Author:** ![Julien\_Reszka](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/julien_reszka/32/116443_2.png) [@Julien\_Reszka](https://discuss.ai.google.dev/u/Julien_Reszka)\
**Post date:** [April 19, 2026, 12:44pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/8 "2026-04-19T12:44:14Z")

</div>

I sympathize. We were ucked by the same issue last night

you can use this script to scan your projects

**Prerequisites** (before running the script):

- Node.js 18+ (uses native `fetch` and top-level `await`)
- Firebase CLI installed and authenticated: `firebase login`
- Google Cloud CLI installed and authenticated: `gcloud auth login`
- You must have Owner or Editor access to the GCP projects you want to scan

**Usage:**

```bash
# Scan deployed JS bundles in your local public/ folder only
node check-gemini-key-exposure.mjs

# Scan all GCP projects linked to your gcloud account
node check-gemini-key-exposure.mjs --all-projects

```

```auto
#!/usr/bin/env node
/**
 * check-gemini-key-exposure.mjs
 *
 * Two-mode check:
 * 1. (default) Scans all deployed JS bundles in public/ for Firebase API keys
 * and tests each against Gemini (TruffleHog PoC).
 * 2. (--all-projects) Uses `gcloud` to enumerate every GCP project, lists all
 * API keys per project via `gcloud alpha services api-keys list`, and tests
 * each key against Gemini.
 *
 * Usage:
 * node scripts/check-gemini-key-exposure.mjs # public/ scan only
 * node scripts/check-gemini-key-exposure.mjs --all-projects # all gcloud projects
 *
 * Exit code:
 * 0 - all keys are blocked (safe)
 * 1 - at least one key is exposed (action required)
 */

import { readdirSync, readFileSync, appendFileSync, writeFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { execSync } from "node:child_process";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, "..");
const PUBLIC_DIR = resolve(ROOT, "public");
const ALL_PROJECTS = process.argv.includes("--all-projects");
const RESULTS_CSV = resolve(__dirname, "gemini-exposure-report.csv");

// Initialize CSV with header (overwrite any previous run)
writeFileSync(RESULTS_CSV, "project,key,models_status,files_status,status\n");

function saveResult(project, key, modelsStatus, filesStatus, exposed) {
  const row = `${project || "public/"},${key},${modelsStatus},${filesStatus},${exposed ? "EXPOSED" : "BLOCKED"}\n`;
  appendFileSync(RESULTS_CSV, row);
}

// -- gcloud helpers ------------------------------------------------------------
function gcloud(args) {
  try {
    return execSync(`gcloud ${args} 2>/dev/null`, { encoding: "utf-8" }).trim();
  } catch {
    return "";
  }
}

function listProjects() {
  const out = gcloud('projects list --format="value(projectId)"');
  return out ? out.split("\n").filter(Boolean) : [];
}

async function getFirebaseApiKeys(projectId) {
  const keys = new Set();

  // Approach 1: firebase apps:sdkconfig WEB (for projects with registered WEB apps)
  try {
    const raw = execSync(
      `firebase apps:list --project "${projectId}" --json 2>/dev/null`,
      { encoding: "utf-8" }
    );
    const parsed = JSON.parse(raw.slice(raw.indexOf("{")));
    const webAppIds = (parsed.result || [])
      .filter((a) => a.platform === "WEB")
      .map((a) => a.appId);
    for (const appId of webAppIds) {
      try {
        const configRaw = execSync(
          `firebase apps:sdkconfig WEB --project "${projectId}" "${appId}" --json 2>/dev/null`,
          { encoding: "utf-8" }
        );
        const configParsed = JSON.parse(configRaw.slice(configRaw.indexOf("{")));
        const apiKey = configParsed.result?.sdkConfig?.apiKey || configParsed.result?.apiKey;
        if (apiKey) keys.add(apiKey);
      } catch {}
    }
  } catch {}

  // Approach 2: /__/firebase/init.json (works even without registered apps)
  if (keys.size === 0) {
    try {
      const res = await fetch(
        `https://${projectId}.firebaseapp.com/__/firebase/init.json`,
        { signal: AbortSignal.timeout(6000) }
      );
      if (res.ok) {
        const json = await res.json();
        if (json.apiKey) keys.add(json.apiKey);
      }
    } catch {}
  }

  return [...keys];
}

// -- public/ scan helpers ------------------------------------------------------
function* walkJs(dir) {
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
    const full = resolve(dir, entry.name);
    if (entry.isDirectory()) yield* walkJs(full);
    else if (entry.isFile() && entry.name.endsWith(".js")) yield full;
  }
}

function extractKeys(filePath) {
  const content = readFileSync(filePath, "utf-8");
  return [...new Set(content.match(/AIzaSy[A-Za-z0-9_-]{33}/g) || [])];
}

// -- Test key against Gemini endpoints (TruffleHog PoC) -----------------------
async function testGeminiAccess(key) {
  const endpoints = [
    `https://generativelanguage.googleapis.com/v1beta/models?key=${key}`,
    `https://generativelanguage.googleapis.com/v1beta/files?key=${key}`,
  ];
  const results = await Promise.all(
    endpoints.map(async (url) => {
      const res = await fetch(url, { method: "GET" });
      return { url, status: res.status };
    })
  );
  return results;
}

// -- Build key map -------------------------------------------------------------
// Map: keyString -> { sources: string[], project?: string }
const keyMap = new Map();

function addKey(keyString, source, project) {
  if (!keyMap.has(keyString)) keyMap.set(keyString, { sources: [], project });
  keyMap.get(keyString).sources.push(source);
}

if (ALL_PROJECTS) {
  console.log("Enumerating all GCP projects via gcloud...");
  const projects = listProjects();
  console.log(`Found ${projects.length} project(s).\n`);
  for (const projectId of projects) {
    process.stdout.write(` ${projectId}... `);
    const keys = await getFirebaseApiKeys(projectId);
    if (keys.length > 0) {
      console.log(`${keys.length} key(s) found`);
      for (const key of keys) addKey(key, `firebase/${projectId}`, projectId);
    } else {
      console.log(`no keys`);
    }
  }
  console.log();
} else {
  console.log("Scanning public/ for Firebase API keys...");
  for (const filePath of walkJs(PUBLIC_DIR)) {
    for (const key of extractKeys(filePath)) {
      addKey(key, filePath.replace(ROOT + "/", ""), undefined);
    }
  }
}

if (keyMap.size === 0) {
  console.log("No Firebase API keys found.");
  process.exit(0);
}

console.log(`Found ${keyMap.size} unique key(s). Testing against Gemini API...\n`);

let anyExposed = false;

for (const [key, { sources, project }] of keyMap) {
  const results = await testGeminiAccess(key);
  const exposed = results.some((r) => r.status === 200);
  const label = exposed ? "EXPOSED" : "BLOCKED";
  const projectTag = project ? ` [${project}]` : "";
  const [modelsResult, filesResult] = results;
  saveResult(project, key, modelsResult.status, filesResult.status, exposed);
  console.log(`${label} ${key}${projectTag}`);
  console.log(
    ` Source: ${sources.slice(0, 3).join(", ")}${sources.length > 3 ? ` (+${sources.length - 3} more)` : ""}`
  );
  for (const { url, status: httpStatus } of results) {
    const endpoint = new URL(url).pathname;
    console.log(` ${endpoint} -> HTTP ${httpStatus}`);
  }
  console.log();

  if (exposed) anyExposed = true;
}

if (anyExposed) {
  console.error("ACTION REQUIRED: At least one key is exposed to Gemini.");
  console.error("Steps:");
  console.error(" 1. Go to Google Cloud Console -> APIs & Services -> Credentials");
  console.error(
    " 2. Find the exposed key and add apiTargets restriction (exclude generativelanguage.googleapis.com)"
  );
  console.error(" 3. Also add HTTP referrer restrictions to limit key usage to your domains");
  console.error(" 4. Re-run this script to confirm the fix");
  process.exit(1);
} else {
  console.log("All keys are blocked from Gemini. Project is safe.");
}

console.log(`\nResults saved to: ${RESULTS_CSV}`);

```

---

<div class="post-metadata">

**Author:** ![Adam\_Hook](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/adam_hook/32/32091_2.png) [@Adam\_Hook](https://discuss.ai.google.dev/u/Adam_Hook)\
**Post date:** [April 19, 2026, 2:20pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/9 "2026-04-19T14:20:54Z")

</div>

We received an unexpected bill for **$13,000 USD** on April 17th. We have only integrated Firebase Remote Templates for AI logic into our Android app, which is currently in its early testing phase with **zero external users**. During our tests, we generated only about 10 pieces of text and images. If this charge cannot be waived, we will be forced to shut down all services, terminate our relationship with Google, and dissolve our studio.

What was supposed to be **Firebase AI Remote Templates** feels more like **‘Remote Terminate Everything’**. This unexpected $13,000 bill is effectively a kill switch for our business.

---

<div class="post-metadata">

**Author:** ![junghyun\_colavo](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/junghyun_colavo/32/118656_2.png) [@junghyun\_colavo](https://discuss.ai.google.dev/u/junghyun_colavo)\
**Post date:** [April 29, 2026, 7:44am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/10 "2026-04-29T07:44:26Z")

</div>

Hi @zanbezi — yours was the first case I read when I started realizing how deep our hole was.  
We’re now staring at a $67k+ charge accrued in 19 hours, and if it gets enforced our small Korean startup doesn’t survive it.

The technical pattern matches yours closely (Firebase-provisioned key, Google’s own May 2024 restriction policy not applied) but honestly the part of your thread that hit hardest was just recognizing the exact shape of crisis we’re in right now. Full case documented in the post above.

If you’re open to it — what actually moved things from the initial denial to a real review on your end? Even one sentence from someone who’s lived through this would help.

Wishing you the best on your final resolution.

---

<div class="post-metadata">

**Author:** ![Thibaut\_Nguyen](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/thibaut_nguyen/32/118835_2.png) [@Thibaut\_Nguyen](https://discuss.ai.google.dev/u/Thibaut_Nguyen)\
**Post date:** [April 29, 2026, 9:01am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/11 "2026-04-29T09:01:42Z")

</div>

Google always said that these Firebase keys were fine to use in the browser. A decade later we enable Gemini for the project and then we got scammed for 3,5K USD . Seriously Google should refund it.

---

<div class="post-metadata">

**Author:** ![zanbezi](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/zanbezi/32/47598_2.png) [@zanbezi](https://discuss.ai.google.dev/u/zanbezi)\
**Post date:** [April 30, 2026, 7:00am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/12 "2026-04-30T07:00:21Z")

</div>

Hey @Logan_Kilpatrick,

Just following up regarding our €54k Gemini API billing case. I sent the details via email two weeks ago and followed up again a few days later, but wanted to check here as well in case it got lost.

Would really appreciate any guidance or if there is someone else on the team we should contact regarding a possible exception review.

Thanks again for offering to take a look.

---

<div class="post-metadata">

**Author:** ![zanbezi](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/zanbezi/32/47598_2.png) [@zanbezi](https://discuss.ai.google.dev/u/zanbezi)\
**Post date:** [May 8, 2026, 8:34am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/13 "2026-05-08T08:34:11Z")

</div>

Sorry to hear you are dealing with this as well. I sent you a DM, happy to chat further there, and wishing you and your team the best.

---

<div class="post-metadata">

**Author:** ![John\_Shelburne](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/john_shelburne/32/121900_2.png) [@John\_Shelburne](https://discuss.ai.google.dev/u/John_Shelburne)\
**Post date:** [May 16, 2026, 5:51am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/14 "2026-05-16T05:51:13Z")

</div>

Can you help me?  
Opened up my billing page for GCP after my bank got in touch with me and found out that I had been charged for $6,486.81. I keep the account associated with GCP insanely low b/c I have always worried about this nightmare scenario. I wasted so many hours setting up alerts and budgets that i think i am done with cloud services and development in general. I promise to shut all accounts down after i get a refund.

 ![image](https://d3qe71uytubmmx.cloudfront.net/original/3X/a/a/aa1838f2d99dcb831b36eb462fa7239d2ec048ef.jpeg)

---

<div class="post-metadata">

**Author:** ![Sendil\_Kumar](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/sendil_kumar/32/128734_2.png) [@Sendil\_Kumar](https://discuss.ai.google.dev/u/Sendil_Kumar)\
**Post date:** [June 2, 2026, 4:04pm UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/15 "2026-06-02T16:04:03Z")

</div>

I am facing a similar issues , I am an individual user and suddenly I got 80x times the billing on Gemini API key usage than usual. I didn’t get an alert of such a spike and unable to find the source !!

---

<div class="post-metadata">

**Author:** ![Takahiro\_Minami](https://d1dlmcr85iqnpo.cloudfront.net/user_avatar/discuss.ai.google.dev/takahiro_minami/32/131421_2.png) [@Takahiro\_Minami](https://discuss.ai.google.dev/u/Takahiro_Minami)\
**Post date:** [June 16, 2026, 4:07am UTC](https://discuss.ai.google.dev/t/unexpected-54k-billing-spike-in-13-hours-firebase-browser-key-without-api-restrictions-used-for-gemini-requests/140262/16 "2026-06-16T04:07:34Z")

</div>

I’m in pretty much the same situation, and honestly I’m scared. I came across this thread while looking into what to do, so thank you all for sharing your experiences — it genuinely helps to know I’m not the only one going through this.

A compromised API key caused a huge Gemini bill on my personal project. I fixed the problem the same day and reached out to support, but several days on I still have no concrete response and I’m just left waiting. As an individual, there is no way I can pay this amount.
