APS-49Required
Limit how often anyone can call your APIs
Put a rate limit on every endpoint that does work for each request, such as APIs, agent endpoints, forms and report collectors.
How to do it
Count requests per client address and per account over a short window, such as 60 a minute. Set tighter limits on costly routes, such as search, sign-up, exports, AI calls and anything that sends email or costs money. Start with 20 searches and 5 sign-ups a minute. Answer 429 Too Many Requests with a Retry-After header when a client goes over, as say when to retry requires. Consider also sending the RateLimit headers, which are still an IETF draft, as tell clients their rate limit says. Behind a proxy, trust its forwarded address, or every visitor counts as one. Logins need more, such as two-factor login, as in slow down password guessing.
Examples
HTTP example
HTTP/2 429
retry-after: 42
ratelimit-policy: "per-minute";q=60;w=60
ratelimit: "per-minute";r=0;t=42
JavaScript example
// Express: one limiter in front of every endpoint that does work
import rateLimit from 'express-rate-limit';
app.set('trust proxy', 1);
app.use(['/api', '/mcp', '/ask'], rateLimit({ windowMs: 60_000, limit: 60, standardHeaders: 'draft-8' }));
Python example
# Flask with Flask-Limiter: 429 and Retry-After past 60 a minute
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
limiter = Limiter(get_remote_address, app=app, headers_enabled=True, storage_uri="redis://localhost:6379")
@app.post("/api/orders")
@limiter.limit("60 per minute")
def create_order(): ...
nginx example
# nginx in front of any stack
limit_req_zone $binary_remote_addr zone=work:10m rate=60r/m;
location /api/ { limit_req zone=work burst=10 nodelay; limit_req_status 429; proxy_pass http://app; }
How to check
Send more requests than the limit in a loop, to an ordinary endpoint and to the search and sign-up routes. The extra ones get 429 with Retry-After, the limit resets after the window, and normal pages are not affected.
Sources
- RFC 6585 (Additional HTTP Status Codes)
- RFC 9110 (Retry-After)
- OWASP API4:2023 Unrestricted Resource Consumption
Related rules
- WEB-228 Collect script errors from visitors' browsers in Website
- API-36 Tell clients their rate limit in API design
- API-73 Limit the connections and messages of each client in API design
- OPS-40 Turn work away before overload in Operations and observability
- PAY-43 Stop card testing on your checkout in Payments and billing
- INF-71 Let only your edge network reach the origin in Infrastructure and environments
- INF-86 Put a web application firewall and flood protection in front of public services in Infrastructure and environments
- NTF-25 Guard text sending against SMS pumping in Notifications
- SCL-8 Keep locks and counters in a shared store in Scaling
- SCL-31 Cap each customer's running and queued jobs in Scaling
- AGT-4 Choose a default for unsigned agents in Agents as users
- AGT-78 Limit bulk reads and scraping in Agents as users
- AGT-72 Answer bursts with 429 instead of a stall in Agents as users
- TNS-1 Put a report button on every piece of user content in Trust and safety
- MED-28 Limit how many uploads each account starts in Files and media
- SRH-11 Cap the size of every search in Search
- APS-26 Collect reports of blocked content in Application security
- API-20 Say when to retry in API design
- APS-10 Slow down password guessing in Application security
- APS-50 Take the client address only from proxies you run in Application security
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/limits-and-business-logic/rate-limits">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.