# API Rate Limiting Is Not Just About 429: Preventing Enumeration, Credential Stuffing, and Resource Abuse

Canonical: https://vulnify.app/blog/api-rate-limiting-enumeration-credential-stuffing-resource-abuse

Design API rate limits around sensitive operations, identities, resource cost, batching, and third-party spending instead of relying on one generic 429 threshold.

A rate limit is not successful merely because an API eventually returns HTTP 429. The real question is whether expensive or sensitive actions are constrained in a way that matches how attackers abuse them. One generic per-IP threshold can leave password guessing, account enumeration, bulk exports, OTP verification, GraphQL batching, and costly third-party operations almost untouched. OWASP's API guidance treats unrestricted resource consumption as a major API risk. It includes more than request count: execution time, memory, upload size, records per page, operations per request, and third-party spending can all require limits. Start with the operation, not the status code Inventory operations that deserve explicit limits: login attempts; password reset requests; OTP or MFA verification; username and email lookup; search and autocomplete; exports and report generation; file uploads and conversions; bulk writes; email, SMS, or phone actions; expensive AI or third-party API calls. Then decide what resource or abuse pattern each limit is protecting. Per-IP limits are not enough IP-based throttling is useful, but attackers can distribute requests across addresses. For account-specific abuse, combine dimensions such as IP, account, email address, API key, device, tenant, or operation. A password reset endpoint may need a limit per destination account as well as a network-level limit. Otherwise an attacker can rotate source addresses and flood one user with reset messages. Credential stuffing needs account-aware controls Credential stuffing uses real username and password pairs stolen elsewhere. A low per-IP rate limit slows one machine but not a distributed botnet. Use MFA, breached-password defenses where appropriate, risk signals, progressive delays, account-specific attempt limits, and monitoring. Avoid permanent account lockouts that can be abused to deny service to legitimate users. Rate limits do not fix account enumeration by themselves If login, signup, password reset, or invite endpoints return distinguishable messages for existing and non-existing users, attackers may enumerate accounts slowly enough to stay below a threshold. Use consistent response behavior where the business flow permits it. Rate limiting is one layer, not a substitute for response design. OTP and MFA verification need their own attempt budgets A six-digit code has a finite search space. Verification endpoints should enforce a small number of attempts per challenge or account and expire the code promptly. Generating a new code should not reset every security counter automatically. Record failed attempts server-side and invalidate challenges according to the authentication design. One request can still be expensive A request-frequency limit does not stop a client from asking for an enormous export, submitting a huge file, requesting one million records, or sending a GraphQL batch with hundreds of operations. Set hard limits on payload size, page size, batch size, query cost, execution time, and output size. GraphQL and batch APIs need work-based limits If one HTTP request contains many logical operations, count those operations or their estimated cost. Sensitive mutations should have their own throttles even when they share one GraphQL endpoint. The same principle applies to REST batch endpoints that accept arrays of jobs or records. Protect third-party spending APIs often trigger paid services such as SMS, email, identity verification, image processing, maps, AI inference, or cloud storage. A technically small request can create a large bill. Configure provider spending limits where available. Add budget alerts. Put per-account and global ceilings around actions that incur external cost. Return useful 429 responses HTTP 429 is the conventional signal for too many requests. Where appropriate, include retry guidance such as a Retry-After header. Do not reveal internal counters or security thresholds that are not needed by legitimate clients. Keep error behavior consistent enough that throttling does not create a new enumeration oracle. Distributed systems need shared counters or consistent policy A limit enforced independently on ten application nodes may allow roughly ten times the intended volume. Use a shared rate-limit store, gateway, or partitioning design that preserves the security boundary across replicas. Plan for store failure. Decide whether sensitive operations fail closed, degrade to a local limit, or use another safe fallback. Separate burst tolerance from sustained abuse Legitimate clients may make short bursts. Token-bucket or similar designs can allow a controlled burst while limiting sustained traffic. Sensitive authentication operations usually deserve much tighter burst behavior than ordinary read endpoints. Use production telemetry to tune limits rather than guessing one global number. Multi-tenant systems should consider tenant-level cost One noisy tenant can consume shared resources without violating a per-user threshold. Apply tenant-level budgets to exports, searches, storage, background jobs, and other expensive operations where appropriate. Fairness controls protect both availability and cost. Monitor behavior near the limit Do not log only the final 429. Track repeated near-threshold attempts, rotating source addresses, many accounts targeted from one network, and one account targeted from many networks. These patterns can reveal credential stuffing or enumeration before a single counter looks extreme. How Vulnify relates to rate limiting Vulnify can assess public application behavior and surface authentication or API security weaknesses that are externally observable within its supported scan scope. A scanner cannot know your cloud budget, per-tenant fairness rules, or acceptable number of password attempts. Use scanning to identify exposed behavior and pair it with application telemetry and business-specific limit design. API rate-limit review checklist Identify sensitive and expensive operations. Use more than IP address where abuse is account-specific. Limit OTP and recovery attempts per challenge or account. Set payload, pagination, batch, and query-cost limits. Protect operations that trigger paid third-party services. Use shared enforcement across distributed application nodes. Allow controlled bursts only where the business flow needs them. Monitor both 429 responses and near-threshold patterns. Test authenticated and unauthenticated limits separately. Retest after gateway or architecture changes. Different endpoints need different budgets A single threshold for an entire API is easy to administer but rarely matches real risk. Reading a public product catalog is not equivalent to attempting a login, generating a PDF report, sending an SMS, or running a complex search. Define limits at the operation level when the cost or abuse pattern differs materially. This also reduces accidental denial of service against legitimate users. A customer browsing quickly should not consume the same security budget used for password guesses. Rate limits should survive alternate routes If the same action exists through REST, GraphQL, mobile endpoints, and a legacy API, the abuse limit should follow the underlying action rather than one route. Otherwise an attacker can switch interfaces when one counter is exhausted. Centralize sensitive attempt counters or enforce them in the service that performs the action. Gateway throttling can remain as an additional network layer. Be careful when using IP addresses as identity Large offices, mobile carriers, VPNs, and privacy relays can put many legitimate users behind one address. Aggressive IP-only limits can therefore block innocent users. At the same time, trusting forwarded-IP headers from arbitrary clients can let attackers choose the address used for rate limiting. Configure trusted proxies explicitly and derive client IP only from headers set by infrastructure you control. The limiter itself needs abuse resistance Creating a unique counter for every attacker-supplied username, token, or random key can consume memory in the rate-limit store. Bound key cardinality, expire counters, and avoid designs where an attacker can create unlimited persistent limiter state. Monitor the health of the rate-limit backend because an overloaded limiter can become the denial-of-service target. Test limits from the client's perspective Record when throttling starts, how long it lasts, whether counters reset unexpectedly, and whether another API route bypasses it. Test with authorized accounts and synthetic identifiers so you do not lock real users or trigger costly third-party actions. After fixes, repeat the exact sequence and verify that the limit applies at the correct business boundary rather than only to one URL. Security limits need workable client behavior Legitimate clients should know how to recover when throttled. Document retry behavior, use exponential backoff where appropriate, and avoid clients that immediately retry every 429 in a tight loop. A badly behaved official client can create the same load pattern as abuse. For asynchronous work, consider queueing or job-status APIs instead of allowing clients to poll expensive operations continuously. Security and reliability improve together when the interface makes efficient behavior the normal path. Treat rate-limit changes as security-sensitive configuration Gateway migrations, new CDN rules, authentication redesigns, and API version changes can silently alter throttling. Include the expected limits in deployment checks and compare production behavior with staging after infrastructure changes. A rate limit that exists only in one proxy is easy to lose when traffic moves around it. Document legitimate exceptions Some machine clients genuinely need higher limits. Give those clients explicit quotas or scoped credentials instead of quietly disabling protection for an entire endpoint. Exceptions should have an owner, reason, expiry review, and monitoring so they do not become permanent bypasses. Revisit limits as the product grows A safe threshold for a small application may become disruptive after traffic patterns change, while a generous threshold can become dangerous when an operation gains new cost. Review limits after major product launches, pricing changes, new integrations, and architecture changes rather than treating the initial values as permanent. Rate limiting is resource governance The best rate limits reflect what the operation costs and what abuse looks like. HTTP 429 is merely the visible response after a policy is enforced. The real security work is deciding which identity, operation, resource, and time window the policy should protect.
