11 min read

I woke up to a $4,200 OpenAI bill. Here's what happened.

A solo founder's API key got scraped from a public commit and used to run gpt-4 calls for two days before they noticed. Total damage: $4,217. Here is the postmortem — how the key leaked, how to detect this, and how to prevent it from happening to you.

This happened to a friend last month. Names changed.

She built an AI chat app with Lovable + OpenAI. Shipped it on Vercel. 12 paying users. She was happy.

Tuesday morning, she opened her email and found two things:

1. An OpenAI billing alert: $4,217 charged. 2. A vague Stripe email: payment failed, your subscription is now past due.

Her OpenAI account had been used to run ~3 million tokens per minute against gpt-4-turbo for 38 hours. Someone else's traffic, on her dime.

Here's what happened, in order.

How the key leaked

Three weeks earlier, she'd asked Cursor to "set up an OpenAI client." Cursor wrote a file like this:

```ts // lib/openai.ts import OpenAI from 'openai';

const client = new OpenAI({ apiKey: 'sk-proj-Ab12...XyZ9', // <- inline literal }); ```

She noticed the inline key during code review and moved it to process.env.OPENAI_API_KEY. She committed the move. She thought she'd fixed it.

She had not. The original commit — with the literal key in the source — was still in the git history. git log -p showed it. GitHub's secret scanner had flagged it; her email filter had hidden the alert.

Within 4 hours of the original commit landing on a public branch, an attacker's bot scraped the key, validated it against OpenAI's API, and added it to a key-rotation pool used to run cheap inference for somebody else's product. The bot was patient — it didn't burn the key immediately. It started slow, then ramped up over three weeks.

Tuesday's $4,217 was the ramp-up to peak usage.

The detection-window math

GitHub's secret scanner does ping OpenAI when it sees the pattern. OpenAI does revoke the key. The problem is the window between "key committed publicly" and "OpenAI revokes the key." For a popular pattern like sk-proj-... on a public repo, that window is typically 30 minutes to a few hours.

For attackers' scrapers, the window is similar. The key gets caught about half the time before the scraper sees it; the other half, the scraper wins. There's no honor system — the bots are racing OpenAI's revocation pipeline, and their hit rate is high enough to keep them running.

Her key was scraped before OpenAI's revocation fired. By the time OpenAI's scanner flagged it, the bot had already exfiltrated it to their pool. Revocation only stops new requests; it doesn't claw back the key from the pool's distribution.

She's not unusual. Forum threads from victims look the same: "I didn't even know the key was committed," "I thought I'd fixed it," "I rotated immediately when I saw the bill."

Step 1 of the response — find every key

Once she saw the bill, she had to assume every key she'd ever committed was compromised. Same exercise applies to anyone reading this. Run:

git log -p --all -S 'sk-' | head -500
git log -p --all -S 'AKIA' | head -200
git log -p --all -S 'ghp_' | head -200
git log -p --all -S 'eyJ' | head -200

Each pattern catches a family — sk- for OpenAI/Anthropic/Stripe, AKIA for AWS, ghp_ for GitHub PATs, eyJ for any JWT (including Supabase service-role).

For each match, treat the key as compromised. Rotate it. Don't bother removing the commit from git history — once a key is public, history rewrites don't help; the key is already in scrapers' pools.

Step 2 — Rotate

For each provider:

  • OpenAI: dashboard → API keys → revoke + create new + update env vars + redeploy.
  • Anthropic: console.anthropic.com → API keys → same.
  • Stripe: dashboard → developers → API keys → roll key. Critical: do this for both live AND test keys; the test key gives webhook access too.
  • AWS: IAM → access keys → deactivate + create new + update env + delete old.
  • GitHub PAT: settings → developer settings → personal access tokens → delete + create new.
  • Supabase service-role: project settings → API → reset.

For each rotation, double-check that the new key is in your deploy environment and that no caching layer (Vercel KV, Redis) holds the old key. The old key stays alive in caches longer than you expect.

Step 3 — Audit usage on each provider

Before you rotate, screenshot the usage page for each provider. You need to know:

  • Which keys saw unusual activity (any spike not from your traffic)
  • The geographic source of the requests (you're in California; the attacker's IP block is in Bangalore)
  • The time window of the abuse

This is your evidence for the support ticket. Most providers will refund "obviously not yours" charges. Be ready to point at the spike + the IP block + the rotation timestamp.

OpenAI specifically: their finance team typically refunds stolen-key charges if you can show the timeline + the rotation evidence + the GitHub commit that exposed the key. Email billing@openai.com from the registered email; expect a multi-day reply.

She got most of the $4,217 back. Lost a few hundred dollars + the time.

Step 4 — Set spending limits NOW

Every provider has a spending cap. Most accounts have it set to "no limit" by default. Fix that now, before anything else:

  • OpenAI: dashboard → billing → usage limits. Set hard limit to 5x your normal monthly spend. Below that, set a soft alert at 2x.
  • Anthropic: console → plans & billing → spending limit.
  • Stripe: dashboard → billing → set radar rules to flag unusual API patterns.
  • AWS: Billing console → budgets → set monthly budget alarm + maximum-spend cap (use AWS Budgets + Service Quotas; the latter actually caps API usage, the former just alerts).

The cap is your last line of defense. If a key leaks again — and statistically, every solo founder will leak at least one key in their first 18 months — the cap is what stops $4,000 from becoming $40,000.

Step 5 — Stop having this problem

The reason this keeps happening is that solo founders cannot manually grep their git history every time they push a commit. The whole reason you use Cursor and Lovable is to NOT have to do that.

Securie handles the secret-leak class as a first-class feature:

  • Secret scanner specialist runs on every PR. It detects every leaked credential pattern in the diff *and* in the existing codebase (not just new commits). Eleven providers covered: AWS, Stripe (live/test/restricted/publishable), GitHub (all PAT types), Slack, OpenAI, Anthropic, Postgres URIs, JWTs, GCP service-account keys, Azure storage keys.
  • Live-key validation runs against the provider's API for every detected key. If the key is active, the finding ships with "Live key confirmed." If the key is already revoked, the finding deprioritizes — you don't waste time on already-mitigated issues.
  • Auto-rotation PR (Indie tier and up) opens automatically: mint new key via provider API, update Vercel/Netlify env vars, revoke old key, comment on the PR with the rotation timestamp.
  • Cost-firewall ledger tracks per-tenant USD spend across providers. If your normal monthly spend is $40 and a key starts burning at $2,000/day, the firewall alerts before the bill hits — the L39 cost firewall is built exactly for this. Soft caps throttle, never charge.

Two minutes to install. Zero engineering work to maintain.

What I told my friend

She knows now to:

1. Set the OpenAI hard limit immediately on every new account. 2. Never let a key sit in source for even one commit. 3. Set up the alert pipeline so she sees billing emails the day they fire.

Most importantly: she didn't quit. The money she lost is the founder-tax for shipping fast. The lesson is to build the infrastructure that prevents the next $4,200 from happening — not to slow down.

Related

Related posts