When more than 543,000 valid credentials surface in public GitHub repositories, the first operational question for many teams is password reset. For APIs, the sharper problem arrives sooner: keys and tokens that still carry the rate budget you assigned when the integrator was trusted. Attackers who find those secrets do not need to break your perimeter. They authenticate as a known client and ride the same quotas that once looked generous for legitimate batch work.
ShinyHunters-style investigations and large consumer breaches keep proving the same pattern. Stolen or exposed credentials turn into sustained API access that looks like partner traffic until someone compares identity, route mix, and object volume. A flat requests-per-minute (RPM) number at the edge rarely tells that story on its own.
When a “healthy” quota hides enumeration
Consider a B2B billing API that issues each partner a bearer token with a 600 RPM ceiling and a short burst window. That design supports nightly reconciliation jobs. After a key lands in a public gist or CI log, operators reuse it from cloud and residential exits. They stay under 600 RPM while they walk customer IDs, invoice endpoints, and export routes. Your gateway counters stay green. Your SIEM sees authenticated success. The abuse signal sits in which objects move and how the path mix shifts, not in raw volume.
Student-loan and consumer-record incidents show how quickly that pattern becomes a disclosure problem. Once an authenticated client can page through PII-bearing resources, the rate limit you set for “partner sync” becomes an extraction budget measured in records per hour.
Telemetry that actually surfaces key abuse
Start from identity-scoped series, not only source IP. Pull these fields into one view for every API key, OAuth client, and service account:
- Requests per minute and per hour, split by route group (auth, read, write, export, admin)
- Distinct object IDs touched per window (accounts, loans, tickets, files)
- Success-to-4xx ratio, especially 401/403 after long clean stretches
- Geographic and ASN diversity for a single client identity
- Burst shape: fixed-window cliffs versus token-bucket smooth draining
- Retry-After compliance when you already emit 429
Credential-stuffing against login and token endpoints shows different telemetry than bulk GET abuse. Stuffing produces high failure rates, short TTLs on source IPs, and pressure on /token or /oauth paths. Key abuse after a GitHub leak produces high success rates, stable client credentials, and a sudden climb in unique resource identifiers under a previously quiet partner ID.
Patch weeks add noise. Microsoft’s large monthly rollups drive scanner and exploit probes that inflate unauthenticated error volume. Keep those campaigns in a separate hunt queue so they do not bury authenticated partner anomalies. CISA advisories on edge appliances matter for your management plane; treat admin API routes with tighter caps and separate alert thresholds from public product APIs.
Choose algorithms for the failure mode you face
Fixed windows are simple and create thundering herds at window boundaries. Attackers learn to send a full budget in the first seconds of each minute. Sliding windows smooth that cliff and raise the cost of synchronized spray. Token buckets fit partner APIs that need short legitimate bursts without granting a sustained extraction rate. Leaky buckets favor steady pacing and punish sudden drains that look like dump scripts.
Map algorithm to route class:
- Authentication and token issuance: strict sliding window per IP and per username or client_id; low burst; long cooldown after repeated failures
- Read APIs over PII or financial objects: token bucket keyed by API identity plus object-scope counters (records per hour, unique IDs per day)
- Write and admin mutations: low fixed or leaky limits with mandatory audit hooks on every allow
- Bulk export and report generation: separate, slower quota with asynchronous jobs and human approval for first-time high volume
Gateway-only RPM treats login, search, and CSV export as equal. That is the myth. A 600 RPM partner cap that freely includes export endpoints will lose to any operator who stays polite on the counter.
Controls you can ship this week
Split trust from quota
Issue keys with explicit scopes and bind each scope to its own limiter. A reconciliation scope may allow invoice list reads at 120 RPM. It should not inherit an open export scope at the same rate. When repository scanning or secret-detection tools flag a key, revoke first, then rebuild with narrower scopes and lower ceilings. Do not restore the original “trusted partner” budget until you review ninety days of that client’s route and object histograms.
Add object-aware secondary limits
Keep the edge volume limiter for availability. Add application-layer ceilings on unique object access per identity. Example: 5,000 customer record reads per hour for a standard integrator, with a hard stop and pager when the identity crosses 2× its thirty-day baseline. Volume can look healthy while object cardinality explodes.
Respond with signal, not only 429
Return 429 with Retry-After and a correlation ID. Log the limiter name, identity, route class, and remaining budget. Watch clients that ignore Retry-After; that behavior separates broken SDKs from hostile automation. For authenticated abuse, prefer temporary key quarantine and step-up review over silent drops that push operators onto a second stolen key.
Tie rotation to exposure events
When public repositories or breach markets list credentials related to your tenants, open an API-identity hunt in the same shift. Match seller infrastructure and dump timestamps to auth spikes on token endpoints, then to elevated object reads under surviving keys. Per-IP blocklists lag when the attacker already holds a valid secret.
A concrete enforcement sketch
At the edge (API gateway or service mesh): enforce identity + route-class token buckets; emit structured 429 events; mirror counters to your metrics backend.
In the application: enforce object cardinality and export job quotas; deny with a distinct error code when the object budget trips while the edge RPM still has headroom.
In the SOC: alert when one client_id gains ASN diversity above baseline, when unique object IDs jump more than 3× week-over-week at constant RPM, or when a previously idle key resumes traffic within forty-eight hours of a public secret finding.
In IAM: short-lived tokens for human-driven tools; longer-lived keys only behind rotation SLAs and secret scanning; separate production and sandbox credentials so a leaked demo key cannot hit live PII routes.
What to verify after you change the caps
Run a tabletop with a simulated leaked partner key. Confirm the key can no longer reach export routes at partner-sync speed. Confirm object-cardinality alerts fire before your disclosure threshold. Confirm on-call can revoke and reissue within one hour without waiting for a full WAF signature pack. Confirm login-path spray and authenticated key abuse land in different runbooks so patch-week scanner noise and stolen-key extraction stay separated.
Rate limiting remains a core availability control. For modern APIs it is also an identity and data-loss control. Treat every key’s quota as a statement of trust that expires the moment that secret leaves your custody, and size the buckets for the routes and objects that secret can still touch.