How Do I Stop One User From Running Up My OpenAI Bill?
6 min read
You add an AI feature, wire it to OpenAI, and it works beautifully in the demo. The question worth answering before real users arrive is blunt: what stops one person from calling that feature ten thousand times and handing you the bill? Right now, for most vibe-coded apps, the answer is nothing. The fix is a rate limit: a cap on how many times each user can hit your AI endpoint in a given window. You need one before you launch.
Why this happens
Your AI endpoint is a button in the browser that, on every click, spends real money on your OpenAI account. The agent that built it wired the button to the model and stopped there, because that is what "add a chat feature" asked for. Nobody asked "and refuse the 500th request in a minute," so nobody built it.
The endpoint has no memory of who is hammering it. A bored user holding down a key, a script someone points at your public URL, or a bot scraping your site all look the same to your server: valid requests, answered one by one, each one billed. There is no daily ceiling on a pay-as-you-go API. The meter just runs.
How to check
-
Open your AI endpoint's code (the server route that calls OpenAI). Look for anything that counts requests per user or per IP. If there is no counter, no
limit, noRetry-After, you have no rate limiting. -
Test it against yourself. In a terminal, fire the endpoint in a loop:
for i in $(seq 1 50); do curl -s -X POST https://your-app.com/api/your-ai-route \ -H "Content-Type: application/json" \ -d '{"prompt":"hi"}' -o /dev/null -w "%{http_code}\n" doneIf all 50 come back
200, nothing is stopping request 5,000. A protected endpoint starts returning429 Too Many Requestsafter a handful. -
Check your OpenAI dashboard for a usage limit. If the monthly hard cap is unset or set to something you would not happily pay, that is your worst case.
The fix
- Require login before the AI feature runs. A limit per user only works if you know who the user is. Gate the endpoint behind auth so every call carries an identity.
- Add a per-user rate limit on the server. For a Vercel or Next.js app, a
hosted counter like Upstash Ratelimit is a few lines: allow, say, 20 requests
per user per hour, and return
429past that. Do this in the server route, never in the browser, where anyone can skip it. - Set a hard spending cap in OpenAI. In the OpenAI dashboard under Billing, set a monthly usage limit. This is your backstop: even if every other control fails, the bill cannot exceed a number you chose.
- Cap the cost of each call. Set
max_tokenson every request and pick the cheapest model that does the job. A single request with an unbounded response can cost more than a hundred small ones. - Re-run the loop test. You should see
200s turn into429s once the limit trips. Confirm a normal user never hits it in ordinary use.
The trap to avoid
Do not put the limit in the browser. Disabling the button after N clicks, or counting in front-end state, stops nobody: an attacker never uses your interface. They call the endpoint directly, exactly like the curl loop above. The counter has to live on the server, tied to the user's identity, where it cannot be skipped. Client-side throttling is a comfort blanket, not a control.
Where this fits
An uncapped AI endpoint is one of the few beta-readiness gaps that costs you money directly rather than just leaking data, and it is invisible in every demo because you are the only one clicking. The free Readiness Report runs this check against your real project and tells you whether your AI route can be drained by one determined user. If you would rather have the rate limit, the spending cap, and the token limits put in place in order, that is what the Finishing Pass is for. It pairs naturally with stopping bot sign-ups and keeping people from tricking the AI itself.