What it is and why it matters
If your app calls an artificial intelligence service, you pay for every request. Not the user who presses the button: you, with the key you put on the server.
Almost always, though, nobody has written a limit. All it takes is a bot, a script left running, someone who clicks a hundred times or who found your key: the requests go out one after another, even at night, and you only notice when the bill arrives.
Claude Code writes the function that calls the AI in no time. The limit, often, it doesn't: it doesn't add it because nobody asked for it. A few lines of code and one setting are enough, but you have to ask for them.
You need two different protections, and one doesn't replace the other:
- The request limit lives in your code. It says: this person can make at most so many requests per minute. Claude writes it.
- The spending cap lives in the AI service's dashboard. It says: this month we don't go past this amount. You set it yourself, by hand.
The limit stops everyday abuse. The cap is the safety net underneath: it saves you when the limit has a hole, or when the key ends up in the wrong hands and someone uses it without going through your app.
1. The limit in the code
Open Claude Code in your app's folder (from the terminal: go into the folder and type claude) and paste this:
My app calls an artificial intelligence service. Find every place in the code where a request to the AI starts. Then: 1. put a per-person, per-minute request limit on those endpoints (per user if they're logged in, per IP address if they're not), with a clear message like 'Too many requests, try again in a minute'; if the app runs on serverless or on multiple instances, the counter must live in a shared store (for example Redis), not in the server's memory; 2. check that the AI key lives only on the server and never in the code that reaches the browser or the phone; 3. at the end, explain to me in simple words what you changed and how to test it.
What it means, in simple words
- Endpoint: it's the address on your server that the app calls when the user presses the button. The check goes there, not in the app: a check in the app or in the browser can be bypassed in a moment.
- Per user or per IP: if the person has logged in, you recognize them by their account. If they haven't, all you have is their IP address. It isn't perfect, because several people can share the same IP and someone who wants to abuse it can change it, but it stops most cases.
- Shared store: on serverless hosting, and in general when the app runs on several copies of the server, each request can land on a different copy. A counter in one copy's memory only sees its own requests and resets when that copy shuts down. That's why it has to live somewhere all of them can see: Redis or your database.
- Key only on the server: anything that reaches the browser or the phone can be read. A key inside the app is, in practice, public. Watch out for the variables some frameworks send to the browser on purpose, like the ones starting with NEXT_PUBLIC_ in Next.js, VITE_ in Vite or EXPO_PUBLIC_ in Expo: the AI key must never go there.
Which number to choose
The prompt doesn't say how many requests per minute. If Claude asks you, think about how many times a real person uses that feature in a minute and leave some margin. Better to start low: raising it later is easy.
The per-minute limit alone isn't enough. A few requests per minute, repeated all night long, still add up to thousands of requests. If the feature is expensive, add a daily limit:
Also add a daily per-user limit on the same AI requests, using the same shared store as the per-minute limit. When it's exceeded, show a clear message like 'You've reached today's limit, try again tomorrow'.
2. The spending cap
You do this one yourself, not Claude. It takes two minutes.
- Go into the dashboard of the AI service you use and look for the billing or usage limits section.
- Set a monthly cap. Pick a number you can afford to lose on a bad night.
- Read what happens when the cap is reached. It's the step almost everyone skips.
Careful: not every service actually stops requests at the cap, some only send you an alert. So don't trust the name of the setting: check in your service's documentation what that limit actually does. The difference is between “blocks requests” and “sends you a notification”.
If your dashboard only offers the alert, use prepaid credits with auto-reload turned off: once the credits run out, the requests stop. Here too, check that your service works this way before you count on it.
If the app uses other paid services, like sending text messages or maps, repeat the same check for each one.
3. The alert
In the same dashboard, if the option is there, turn on an alert email for when you reach half of the cap. Send it to an address you actually read.
The reason is simple: a cap that blocks requests also stops your real users. The halfway alert gives you time to figure out what's going on, whether it's real people using the app or abuse, before the app stops responding.
4. The test
A limit you haven't tested is a limit you don't know exists. Ask Claude:
Write a small script that calls my AI endpoint 30 times in one minute with the same test user and tell me how many requests get through and how many are blocked.
If they all get through, the limit isn't working: have it fixed before you publish.
- Run the test on the local or test version of the app, not on the one your users use. The requests that get through really do reach the AI.
- If the limit you chose is higher than 30 per minute, raise the number in the test too, otherwise they all get through even with the limit working.
- If the app runs on serverless, repeat the test on the online test version too. Locally there's only one server, and a counter kept in memory seems to work even when it wouldn't work online.
Common mistakes
- Putting the limit only in the app or the browser. It's easy to get around: the check goes on the server, in the endpoint.
- Keeping the counter in the server's memory on serverless. Each copy counts on its own: use Redis or the database.
- Setting the budget in the dashboard and thinking you're covered. Sometimes it's only an alert: check what it actually does and, if needed, use prepaid credits without auto-reload.
- Leaving the AI key in the app's code or in a public variable. Move it to the server and, if it has already shipped in a published app, delete it from the dashboard and create a new one.
- Protecting one endpoint and forgetting another. Have Claude list every place a request to the AI starts from and check that the limit is on each one.
- Not testing the limit. Run the test script before you publish, every time you change those parts.
Checklist
- Request limit on every endpoint that calls the AI
- Counter in a shared store (Redis or database)
- AI key only on the server
- Monthly cap set in the service's dashboard
- Checked whether the cap blocks requests or only sends an alert
- Alert email at half of the cap
- 30-request test done, with some of them blocked
Frequently asked questions
I have few users, do I really need this?
Yes. The problem isn't real users, it's bots and scripts, which don't care how many users you have. An endpoint open on the internet can be found even if nobody knows the app.
What is Redis? Do I have to install something?
It's a very fast store, often used precisely for counters. Many hosting providers offer it as a ready-made service. If you don't want to add anything, the database you already use works too: tell Claude and it will use that for the counter.
Does the limit slow down the app?
Not for people using it normally: it's a very quick check before the request, and nobody notices it. Only someone who goes over the limit notices, and they see the message.
If the cap blocks everything, does my app stop working?
The part that uses the AI does, until you raise the cap or top up the credits. That's why you need the halfway alert: it gives you time to tell whether it's real growth or abuse, before it happens.