How to stop a runaway OpenAI / API bill in your app
Calling a paid AI API directly from the browser exposes your key and your budget. Here is the safe pattern: a server proxy, rate limits, and spend caps.
If your app calls OpenAI, Anthropic, or another paid API directly from the browser, two bad things are true: your API key is exposed, and anyone can run up your bill. AI builders sometimes wire it this way because it’s the quickest path to a working demo.
The fix: never call a paid API from the client
- Put the API key in a server-side function (an API route / Edge Function). The browser calls your endpoint; your endpoint calls the paid API with the key.
- Add rate limiting on that endpoint (per IP / per user) so one visitor can’t fire thousands of requests.
- Require auth for anything that costs money.
Set spend limits as a backstop
- OpenAI: set a monthly usage limit and billing alerts in your account.
- Use a separate, restricted key per app so you can kill it without breaking everything.
If your key is already in the client
Rotate it immediately, then move the calls server-side. A key that has been public should be considered compromised.
Shipshape flags when your page appears to call a paid API directly from the browser (a "needs-deeper" warning — it never calls the API itself). → Scan your app
FAQ
Why is calling OpenAI from the browser dangerous?
It exposes your API key to anyone who views the page, and it lets any visitor spend on your account. Paid APIs should be called from a server endpoint that holds the key.
How do I prevent a runaway AI bill?
Proxy the call through a server-side endpoint, require auth, rate-limit it, and set a monthly usage cap and billing alerts on the provider account.
My API key is already in my frontend — what now?
Rotate it immediately (treat it as compromised), then move the call server-side behind auth and rate limiting.
Related questions
- My .env file is downloadable — what to do
- pk_test in production: why your checkout silently fails
- How to choose a security scanner for a vibe-coded app
- How do scanners safely test your database rules?