Test rate limits with a small, scoped burst against one endpoint using one test account, and stop at the first throttle response. You are proving a limit exists, not stress testing. Check sensitive actions first, then look for bypasses such as batching or alternate hosts.
What a limit test should prove
OWASP's API risk for unrestricted resource consumption lists the controls to look for: rate limits per client within a time frame, stricter ones for sensitive endpoints, caps on payload and parameter sizes, and server-side validation of record counts. Throttling for operations such as one-time password validation and password recovery is called out specifically.
So a good test asks four questions:
- Does a limit exist at all on this endpoint?
- Is it tighter on sensitive actions than on ordinary reads?
- Does the API say so with a 429 response?
- Can the limit be sidestepped?
Keep the test safe
This is where people get into trouble. One major cloud provider's testing policy prohibits flooding, including login and API request flooding, and another prohibits network-intensive automated testing. A rate limit test that turns into a flood breaks those rules and can take down your own service.
Use a cautious method:
- One test account, so you do not lock out real users.
- Start with a low number of requests and raise it in steps.
- Stop when you see the first 429 or the first slowdown.
- Run it in a test environment where possible, and tell whoever watches production alerts.
- Set a hard ceiling in the tool so a typo cannot multiply the traffic.
Bypasses to try
| Idea | What you are checking |
|---|---|
| Same action through a different route | Is the limit on the endpoint or on the business action? |
| Older API version or beta host | Do they share the same limiter? |
| Batched operations | One HTTP request carrying many operations may count as one |
| Changing a client identifier | Is the limit tied to something the caller can change? |
| Concurrent requests | Does a burst slip through before the counter updates? |
The GraphQL guidance is a good example of batching. OWASP says HTTP request counts alone miss expensive queries and multi-operation requests, and recommends capping the number of operations that can be batched, and preventing batching entirely for sensitive operations such as login or OTP checks.
Reading the result
Record the number of requests allowed, the response when the limit hits, any Retry-After header, and how long until the counter resets. Also check what the 429 body reveals. It should not include internal details.
Money limits count too
The same OWASP risk page recommends spending limits or billing alerts for third-party API integrations. If your API calls a paid service on behalf of users, test whether one user can drive that spend.
Where it belongs
A rate limit regression check fits in a nightly job against a test environment: fire a small burst, expect a 429 by request N, fail otherwise.
Worked example: a login endpoint and a report export
A team wants to know whether two endpoints have working limits: the login route and a report export route that triggers a heavy database query. Here is a cautious sequence.
Preparation. They create one test account. They tell the platform owner the test will run on staging for fifteen minutes. They set the test client to a hard ceiling of 20 requests per second, so a mistake cannot become a flood. They decide the stop rule: stop at the first 429, or after 200 requests, whichever comes first.
Login endpoint test. The test client sends wrong passwords for the test account in a loop. They record the request number at which something changes. In this example the server accepts the first five attempts, then returns 429 with a Retry-After header of 60 seconds. That is a pass for the basic limit. They then check three bypasses:
- Does the limit follow the account, or only the source address? They change the source address, as allowed in their test environment. If the limit resets, an attacker with many addresses can keep guessing.
- Does the older v1 login route share the limiter? They test it and find it does not: v1 never returns 429. Finding one.
- Does a batched or alternate form of the login call count once? The API has no batching, so this item is not applicable, and they note that.
Export endpoint test. Rate per second is the wrong lens here, because one request is expensive. They send three exports at once and watch the response time and server load graphs. The third request takes much longer than the first. There is no per-user concurrency cap. Finding two: the export route needs a limit on concurrent runs and on exports per hour.
What they record.
| Endpoint | Limit found | Bypass tested | Result |
|---|---|---|---|
| POST login v2 | 5 attempts then 429 | Change source address | Limit resets, weak |
| POST login v1 | None | Not applicable | Fail |
| POST export | None | Concurrency of three | Fail |
Fixes. One shared limiter for all login versions keyed by account and address together, a lockout or slow-down for repeated failures, and a per-user cap on concurrent exports with a daily quota.
Keeping it repeatable
Turn the login test into a nightly check that sends six wrong attempts to a test account and expects a 429 on the sixth. If someone removes the limiter in a refactor, the next morning's run fails. Keep the export concurrency test manual, because it measures load and needs a person to read the graphs.
The lesson is that rate limit testing is about a few well chosen, low-volume probes against the endpoints that matter, not about volume.
Try it yourself
Open the demo and look at how an example scan labels each finding by endpoint. Imagine the login and export rows from the example above appearing there.
Click through it at your own pace, or open it full screen.
Quick check
Should a rate limit test run against production?
Avoid it where you can. If you must, use one test account, a very small burst, and prior notice to the people who watch alerts.
Is a 429 on every endpoint good enough?
Not necessarily. Login, reset and OTP endpoints need tighter limits than ordinary reads, and the limit should not be bypassable through batching or alternate routes.
How do I know the right limit number?
Start from legitimate use: the most a real user does in a minute or an hour, then add a margin. Sensitive routes such as login and password reset need much tighter limits than ordinary reads.
Sources
- OWASP API4:2023 Unrestricted Resource Consumption
- OWASP GraphQL Cheat Sheet
- AWS penetration testing policy
- Microsoft Cloud penetration testing rules of engagement
Last reviewed: 19 October 2026.