rate-limiting
Skill0 starsChanged 4 months agoMITView on GitHub
Protect a service from overload and abuse with rate limits — choose an algorithm (token bucket or sliding window, not a naive fixed window that leaks a double-burst at the boundary), key the limit by the right identity (user, IP, or API key), return 429 with a correct Retry-After contract so well-behaved clients back off, and set the ceiling from measured capacity rather than a guess. Invoke when exposing an endpoint that untrusted or high-volume callers reach. Skip for internal, low-volume calls with no abuse or overload risk. Server-side self-protection — the counterpart to resilience, which is the caller side.
12 other projects use a version of this.
Adopted by
Most recently taken up by
- mahumadad/quarrytheir own version, first seen Nov 8, 2026
- AnyOrgName/php-skillstheir own version, first seen Nov 8, 2026
- d4rken/clankermuxtheir own version, first seen Nov 2, 2026
- jars-demo/jars-skillstheir own version, first seen Oct 25, 2026
- gerard-labs/superpowers-api-platformtheir own version, first seen Oct 13, 2026
- chunkydotdev/email-skillstheir own version, first seen Sep 29, 2026
Used with
Projects with it also use input-validation.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
hereshecodesSkillinput-validation
Use when processing user input, form data, file uploads, or query parameters
Also in 3 projects that use this
Other versions12 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 12
- harperaa/secure-claude-skills★ 16same name
- d4rken/clankermux★ 11same name
- wpank/ai★ 11same name
- YepAPI/skills★ 8same name
- chunkydotdev/email-skills★ 7same name
- AnyOrgName/php-skills★ 4same name
- ffsshhttiikk/opencode-agents-skills★ 2same name
- gerard-labs/superpowers-api-platform★ 2same name
- kimtth/agent-skill-100-lines-or-less★ 2same name
- mahumadad/quarry★ 1same name
2 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository82 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibilityCursor rule
- api-designCursor rule
- background-jobsCursor rule
- strict-modeCursor rule
- cachingCursor rule
- ci-pipeline-authoringCursor rule
- memory-safetyCursor rule
- code-reviewCursor rule
- concurrency-safetyCursor rule
- containerizationCursor rule
- continuous-learningCursor rule
- resource-safetyCursor rule
- nullable-reference-typesCursor rule
- database-transactionsCursor rule
- data-modelingCursor rule
- datetime-handlingCursor rule
- error-debuggingCursor rule
- eval-harnessCursor rule
- feature-flagsCursor rule
- git-commit-craftCursor rule
- error-handlingCursor rule
- incident-responseCursor rule
- null-safetyCursor rule
- vanilla-safetyCursor rule
- null-safetyCursor rule
- mcp-authoringCursor rule
- observabilityCursor rule
- perf-profilingCursor rule
- security-essentialsCursor rule
- powershell-strict-modeCursor rule
- type-safetyCursor rule
- rate-limitingCursor rule
- release-notesCursor rule
- resilienceCursor rule
- nil-and-exception-safetyCursor rule
- error-handlingCursor rule
- search-firstCursor rule
- secrets-managementCursor rule
- security-reviewCursor rule
- session-handoffCursor rule
- injection-safetyCursor rule
- strategic-compactCursor rule
- supply-chain-securityCursor rule
- optionals-and-memoryCursor rule
- tdd-workflowCursor rule
- state-and-plan-safetyCursor rule
- test-writingCursor rule
- strict-modeCursor rule
- verification-loopCursor rule
- config-safetyCursor rule
- accessibilitySkill
- api-designSkill
- background-jobsSkill
- cachingSkill
- ci-pipeline-authoringSkill
- code-reviewSkill
- concurrency-safetySkill
- containerizationSkill
- continuous-learningSkill
- The first 60 shown.
The file
Files
Rate limiting
An endpoint with no ceiling is an endpoint one caller can turn into an outage. A retry loop gone wrong, a scraper, a misconfigured client, or a deliberate flood — each is the same shape: more requests than you provisioned for, all at once, until the service falls over for everyone. A rate limit is the valve that keeps one caller's volume from becoming everyone's downtime. It is server-side self-protection: you decide the maximum rate you will serve a given caller, and you say no — cheaply and clearly — past it. The failures this prevents: a single client saturating your capacity into an outage; a fixed-window limiter that lets a double-burst through at the window boundary; a 429 with no Retry-After, so even well-behaved clients cannot tell when to come back.
Rate limiting is the counterpart to resilience: resilience is how a caller survives a dependency that is slow or down (timeouts, retries, backoff); rate limiting is how a service protects itself from callers who send too much. The two meet at the 429 — the server sets the Retry-After, and a resilient client honors it.
When to invoke
- Exposing an endpoint that untrusted or high-volume callers reach — a public API, a login or signup route, a password-reset or OTP send, an expensive search or export, anything a script can hammer.
- Adding a per-caller quota, a burst limit, or abuse protection to a surface that currently has none.
- Diagnosing an outage or cost spike caused by one caller's volume, or a limiter that lets bursts through at the window edge.
When NOT to invoke
- Internal, low-volume calls between services you control, on a trusted network, with no abuse or overload risk — a limiter there adds a failure mode and a shared-counter dependency for no protective gain.
- A batch job or admin path where the caller is you and the volume is bounded by design. Match the control to the risk; not every endpoint needs a valve.
Pick the algorithm deliberately
The algorithm decides how you count, and the naive choice has a hole.
- Token bucket — the default for most APIs. A bucket holds up to
Ntokens and refills at a steady rate; each request spends one, and a request with no token available is rejected. This allows a controlled burst (up to the bucket size) while bounding the sustained rate (the refill rate) — which matches how real clients behave. Two numbers,capacityandrefill rate, and they map directly to "how big a burst" and "how fast sustained." - Sliding window — when you need a precise rolling limit. Count requests over the trailing
Tseconds continuously, so "100 per minute" means any 60-second span, not a calendar minute. More accurate than fixed window and free of its boundary hole; a sliding-window log is exact but stores a timestamp per request, while a sliding-window counter approximates it cheaply by weighting the previous window. - Not a naive fixed window. A fixed window ("100 per minute, counter resets on the minute") is simple and wrong at the edge: a client sends 100 in the last second of one window and 100 in the first second of the next — 200 requests in two seconds, twice the limit, straddling the boundary. If you use fixed windows anyway, know this double-burst is the cost, and prefer sliding or token bucket where the burst matters.
- Leaky bucket when you must smooth output. A queue drained at a fixed rate shapes bursty input into a steady stream — useful when the thing you protect (a downstream, a device) needs an even rate, not just a bounded total.
Key the limit by the right identity
A limit is only as good as the thing it counts against. Choose the key so one caller cannot masquerade as many, and so many callers do not get lumped into one.
- Authenticated calls: key by the stable principal — the user id, the API key, the account/tenant. This is the identity you are actually protecting capacity against, and it survives IP changes.
- Unauthenticated calls: key by IP, knowing its limits. IP is the best you have before auth, but it is coarse — a NAT or corporate proxy puts thousands of users behind one address (so a per-IP limit punishes them all), and an attacker with a botnet or a cloud account has thousands of IPs (so a per-IP limit barely slows them). For login and signup, layer it: per-IP and per-account, so neither a single IP nor a single targeted account can be hammered.
- Trust the source of the identity, not a spoofable header. Read the client IP from your infrastructure's trusted forwarding chain (the real
X-Forwarded-Foryour load balancer sets), not a raw request header a client can forge. An API key comes from your auth layer, not from an unauthenticated claim. - Layer limits by scope. A per-key limit protects fairness between callers; a global limit protects the service's absolute capacity; a per-endpoint limit protects an expensive route independently of a cheap one. They compose — a caller under its own quota can still be shed by the global ceiling when the whole service is at capacity.
The 429 contract
Rejecting is half the job; telling the caller how to behave is the other half. A well-designed 429 lets a good client back off correctly and a good ecosystem stay healthy.
- Return
429 Too Many Requests, not 200, 403, or 500. The status is the contract: it tells the client this is a throttle, not a bug or a permission problem, and that the same request will succeed later. A rejected request must be cheap — reject before the expensive work, so shedding load actually sheds it. - Always send
Retry-After. The response must say when to come back —Retry-After: 30(seconds) or an HTTP date. Without it, a client can only guess, and guessing means either giving up on a request that would have succeeded or retrying immediately and deepening the overload. TheRetry-Afteryou send is the value a resilient client is waiting to honor. - Expose the limit state on every response with the conventional headers —
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset(or theX-RateLimit-*variants your ecosystem uses). A client that can see it is running low can self-pace before it hits the wall, which is better for both sides than discovering the limit by being rejected. - Make the reject path itself cheap and abuse-resistant. The check must cost far less than serving the request, or the limiter becomes the amplifier. Do not do expensive work (or emit an expensive log line per rejection) on the path a flood takes.
Set the ceiling from measured capacity
The number is not a vibe. A limit set too low throttles legitimate traffic and generates support tickets; set too high, it does not protect anything and the first real flood finds the real ceiling for you.
- Derive the limit from load you have measured — the throughput at which latency stays acceptable and the service stays healthy, with headroom. Load-test to find where it degrades, then set the ceiling below that knee, not at a round number someone liked.
- Separate the sustained rate from the burst. Most legitimate clients are bursty — a page load fires several calls at once. The token bucket's two knobs let you allow the burst while bounding the sustained rate; a single flat "per second" number forces you to choose one and get the other wrong.
- Set it per-tier where callers differ. A free tier, a paid tier, and an internal service have different legitimate volumes; one global number is wrong for all three. Make the limit a property of the caller's plan.
- Fail open or closed on purpose when the limiter itself breaks. If the shared counter store (Redis) is down, decide in advance: fail open (serve the request, lose enforcement) to protect availability, or fail closed (reject) to protect a fragile downstream. Both are defensible; an undecided default is not.
Anti-patterns
- No limit on a public or expensive endpoint — the endpoint one client can saturate into an outage. The most common and most costly omission.
- A naive fixed window whose boundary lets a double-burst through — 2× the intended rate across the window edge, exactly when a burst hurts most.
- A 429 with no
Retry-After— a well-behaved client is left guessing, so it either backs off too long or retries into the overload it caused. - Keying only by IP for authenticated abuse, so a NAT'd office is throttled as one attacker while a botnet with a thousand IPs sails under the per-IP limit.
- Trusting a spoofable client header for the identity or the source IP, so a caller forges its way around its own quota.
- A limit number pulled from thin air, never traced to measured capacity — too low and it throttles real users, too high and it protects nothing.
- An expensive reject path — heavy work or a per-request log line on the rejection, so the limiter amplifies the flood instead of shedding it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

