Agent Wranglers

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

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.