Protect your API budget from a single runaway user with a real daily query cap, including the boundary case that trips up naive implementations.
1Why Per-User, Not Just Global
A global rate limit protects your total spend but doesn't stop one misbehaving user from consuming the entire budget before anyone else gets a turn. A per-user daily cap ensures that even a runaway script hitting your chatbot repeatedly only affects that one user's own allowance, leaving the rest of the team unaffected.
2Boundary Conditions Matter
Alice's exact-50 case is deliberate: it's the single most common off-by-one bug in rate limiting code. Using <= instead of < (or vice versa) shifts whether a user's very last allowed query is their 50th or their 51st. Get this wrong in either direction and you either let users exceed the intended budget by one, or block them one query early — both worth catching with an explicit boundary test.
3Step-by-Step Breakdown
One Employee Shouldn't Drain the Budget. Every RAG query costs real money: an embedding call plus a generation call. Without a per-user limit, one employee running an automated script against your chatbot could burn through your entire monthly API budget in an afternoon. A daily query cap per user is the simplest real defense.
Enforce a Daily Query Budget. Alice has already used exactly 50 of her 50 allowed queries today — the boundary case. Finish can_query(): a user should be allowed only while their usage is strictly below the daily limit.
Why does a brand-new user ('new-user@nexora.com', never seen before) get ALLOWED instead of causing an error?
- →usage_today.get(user, 0) returns a default of 0 for any user not yet in the dictionary, so a first-time user is correctly treated as having used zero queries instead of crashing.
- →New users are hardcoded as a special case elsewhere in the function.
Module 4 Complete. Your pipeline now handles oversized documents, resists indirect prompt injection, and protects your API budget. Module 5 closes out the masterclass: testing your pipeline systematically and shipping it.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Communicate Rate Limit Status as Real Text, Not an Icon Alone
When a user hits their daily query limit, show the actual limit and reset time as real text content, not just a disabled button, so screen reader users understand why the chatbot stopped responding.
<p>Daily limit reached (50/50). Resets at midnight UTC.</p>SEO Implications
- 1
Target 'per-user API rate limiting' as a distinct search
Developers search for user-scoped (not just global) rate limiting specifically once they've shipped a chatbot and seen uneven usage patterns.
Best Practices
Always Explicitly Test Boundary Conditions in Rate Limiting Logic
A rate limiter that's only tested with usage far below or far above the limit can hide an off-by-one bug at the exact boundary — always include a test case where usage equals the limit exactly.
Frequent Bugs
Using `<=` instead of `<` (or vice versa) in a rate limit check, silently allowing one extra query or blocking one query too early.
Decide explicitly whether the limit is inclusive or exclusive, write it as `used < LIMIT` (allows exactly LIMIT queries) or `used <= LIMIT` (allows LIMIT+1), and add a test at the exact boundary to confirm which behavior you actually shipped.
Real-World Examples
Automated Script Abuse
An employee accidentally leaves a testing script running overnight, hammering the chatbot with requests — a per-user daily cap limits the damage to that one user's allowance instead of exhausting the team's shared API budget before morning.
if not can_query(user): return "Daily limit reached."