Which limit protects against which problem?
‘100 requests per minute’ says little if one call either reads a small record or starts an export of millions of rows. A limit must match the potential harm. For resource consumption, OWASP distinguishes runtime, memory, response volume, concurrent work and third-party spending, among other factors. OWASP API4:2023.
On narrow screens, scroll the table sideways.
| Protection task | Suitable limit | What alone is insufficient |
|---|---|---|
| One user crowds others out with many calls. | Call budget per reliably determined identity or organisation | Only one global counter for all customers |
| A single export ties up the service. | Record/size limit, runtime budget and bounded export concurrency | Counting it like a cheap status query |
| Sending or external computation incurs costs. | Business budget and technical spending limit before execution | An informational header after costs have arisen |
| Too many jobs await processing. | A bounded queue and controlled rejection | Accepting without limit and hoping for later capacity |
| An allowed flow is exploited through automation. | Rules per business action and state | Only a blanket limit per IP address |
Planned values initially remain assumptions. Measure which resources normal flows need in your own environment. A multi-step import must not unexpectedly stall after its first part. Also define which function should remain usable when budget is scarce, such as querying an already accepted job.
Who consumes the budget?
After successful authentication, use a server-determined identifier, such as organisation plus operation class. A freely supplied tenant ID is no reliable basis; otherwise clients could select another counter. Tenant isolation must therefore be established before business-level counting.
A per-key budget can make sense. But it does not protect an organisation-wide limit if the same organisation can create many keys. Conversely, an IP-only limit may affect several legitimate users behind shared access. Coarser network limits still help anonymous entry points; they need a different error and abuse assessment from an authenticated customer budget.
Layers can cooperate: an early limit before expensive authentication, then a user or tenant budget, plus a global capacity limit. Document which layer decides. Behind proxies, caller-supplied forwarding headers must not arbitrarily determine the counting key; the gateway guide explains this trust boundary.
With multiple instances, counting must match the promised limit. A per-process counter gives no shared service-wide ceiling. Check the consistency of the storage used and the decision on its failure. Allowing an expensive write function without limits and briefly continuing harmless reads are different risk decisions.
Distinguish time windows, bursts and concurrency
A fixed window is easy to understand but may allow two immediately consecutive allowances at its boundary. A sliding window reduces these jumps. A token bucket accumulates permissions up to a defined capacity; short bursts are possible while the long-term rate remains limited. A concurrency limit instead protects occupied execution slots without alone limiting hourly call volume. The official ASP.NET Core rate-limiting documentation describes these differences, for example.
A synthetic token-bucket sequence illustrates the decision: capacity two, initially two tokens, refill one token per second, each call costs one token. Three calls at time 0 produce ‘allow, allow, reject’. After half a second, half a token is still insufficient. At second 1, another call is possible. These numbers illustrate the mechanism; they are no production recommendation.
For expensive endpoints, assign different costs or maintain separate budgets. Also bound the number of operations inside batch requests. Otherwise one permitted HTTP call may mean considerable work. Hard size and runtime limits remain necessary alongside counting; a queue must not merely relocate memory consumption.
Implementations differ. nginx may initially delay excess requests, offers an observation mode and defaults to 503 rather than 429 for rejected requests. Even an active rule may therefore look different from an expected header. nginx: ngx_http_limit_req_module.
Handle 429 and Retry-After correctly
HTTP status 429 indicates too many requests within a period. The response may use Retry-After to specify how long a client should wait. RFC 6585 mandates neither a particular counting key nor a universal call allowance. Under overload, a server need not answer every call with 429 either. RFC 6585, sections 4 and 7.2.
An illustrative response might look like this:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
Cache-Control: no-store
{"code":"request_budget_exhausted","request_id":"demo-req-17"}
Retry-After supports a number of seconds or an HTTP date. The example uses 30 seconds; this does not promise every later call will succeed. The server may again be overloaded. A client also needs a bounded retry budget and, for writes, a safe contract against duplicate effects. RFC 9110, section 10.2.3.
Other limit headers can clarify budget and reset time. Their names, units and meaning must fit the API contract. On 27 September 2026, the IETF proposal ‘RateLimit header fields for HTTP’ was active Internet-Draft -11, not a completed RFC. Do not present a draft or provider-specific X-RateLimit headers as a universally binding standard. IETF draft status.
Introduce and verify a rule in a controlled way
First write a short contract: affected operation, counting key, time/cost budget, permitted burst, error response and responsible team. Alongside the target value, specify which legitimate user action must remain possible. This gives more meaningful evidence than a screenshot of a limit header.
In an isolated test environment, small deterministic cases can check whether two test tenants have separate budgets, an exhausted budget stops expensive work before it starts, and access resumes after the intended interval. Use a controllable clock if supported. This tests boundaries without rapid request loops against a public service.
If multiple instances should enforce a shared limit, their interaction belongs in a separate integration test. Also test counter failures, delayed jobs and a full buffer. This system testing was not performed for this article's examples.
Where technically possible, introduce changes in observation mode first, then for a bounded group of your own users. Observation mode means the rule does not yet protect. Preserve the previous configuration and a named rollback path. On unexpected rejection, correct keys, costs or thresholds specifically; do not disable all resource limits without a decision.
A managed gateway may also use soft limits. AWS, for example, explicitly describes API Gateway usage-plan throttling and quotas as ‘best effort’, not a hard spending ceiling. Check such conditions before promising a budget cap. AWS: Usage plans.
What a public finding proves
An observed 429 or limit header proves the particular response. It proves neither shared counters nor effective limits across all roles, endpoints or business flows. Conversely, a missing header does not prove a missing limit.
The API Security Check observes selected public response characteristics. It does not demonstrate actual rate-limit enforcement or perform load testing. Your rule needs configuration evidence, controlled functional cases of your own and, where appropriate, a separately planned capacity test.
If several individually permitted calls still produce an unwanted business flow, read securing business logic. Logging and monitoring explains useful observation of rejections and effects.
Sources and date
Sources checked: 27 September 2026. Links also appear alongside the statements they support.