Rate Limiting
API token rate limiting combines shared guards with operation-specific limiter buckets. The service applies these limiters in the public verification route and in the authenticated creation and management routes.
This page documents the apiTokens and
rate_limiters.apiTokensLimiters configuration only. The generic limiter
engine, strike cache, and helper utilities are documented on the
Rate Limiting page.
guard() and consumeOrReject().
That means API token rate limiting always fails with 429 Too Many Requests,
not with the 400, 401, or 403 responses documented on the endpoint
pages.API token limiter map
The API token system uses eight limiter objects plus one verification switch.
Seven buckets are operation or verification limiters, and one bucket is the
shared generalUnionLimiter that sits in front of most API token routes.
| Flow | Route | Limiter objects | Success reset behavior | Mentioned on |
|---|---|---|---|---|
| Verification | GET /api/public/verify | consumptionRateLimiter and optional generalUnionLimiter | consumptionRateLimiter is reset on success for ${req.ip}_verify, apiKey, and providedIpAddress. The optional union limiter is not reset. | Verifying Tokens |
| Token creation | POST /api/manage/new-token | generalUnionLimiter, operationRateLimits.newTokenCreationLimiter | resetApiUnionLimiters(req.ip!) resets only the union limiter. | Creating Tokens |
| Token listing | GET /api/manage/list-metadata | generalUnionLimiter only | No limiter is reset on success. | Token Listing |
| Revocation | POST /api/manage/revoke | generalUnionLimiter, operationRateLimits.revokeTokensLimiter | resetApiUnionLimiters(req.ip!) resets only the union limiter. | Revocation |
| Metadata | POST /api/manage/metadata | generalUnionLimiter, operationRateLimits.getMetadataTokenLimiter | resetApiUnionLimiters(req.ip!) resets only the union limiter. | Extensive Metadata |
| Rotation | POST /api/manage/rotate | generalUnionLimiter, operationRateLimits.rotationRateLimiter | resetApiUnionLimiters(req.ip!) resets only the union limiter. | Rotation |
| IP restriction updates | POST /api/manage/ip-restriction-update | generalUnionLimiter, operationRateLimits.ipRestrictionUpdate | resetApiUnionLimiters(req.ip!) resets only the union limiter. | IP Restriction Updates |
| Privilege updates | POST /api/manage/privilege-update | generalUnionLimiter, operationRateLimits.privilegeUpdate | resetApiUnionLimiters(req.ip!) resets only the union limiter. | Privileges |
operationRateLimits bucket. The controller goes straight from
generalUnionLimiter to getAllValidTokensList(userId).Failed rate-limit responses
The first limiter rejection comes from consumeOrReject(). It catches the
underlying limiter error, computes retry as
Math.ceil(err.msBeforeNext / 1000), and sends the standard API token
rate-limit response:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 60
{
"error": "Too many requests",
"retry": 60
}
The API token controllers then pass that same key back through guard() on
later requests. guard() stores consecutive limiter failures in an
action-specific cache and escalates the key into limiter.block() when the
strike threshold is reached.
For API token routes, generalUnionLimiter and consumptionRateLimiter use
maxBans = 1. That means the first actual limiter rejection adds the key to
the permanent block cache after the numeric 429 response is sent. The
operation-specific limiters use maxBans = 2, so the second consecutive
rejection creates the permanent block.
After that cache entry exists, guard() rejects the request before consuming
new points and reuses the cached expiry marker. Because the API token
controllers do not pass a custom seconds value to guard(), that cached
marker becomes permanent:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: permanent
{
"error": "Too many requests",
"retry": "permanent"
}
This is why API token rate-limit failures stay in the 429 family even after
the key has been escalated into a long-lived block.
How each limiter is used
Each limiter has a different job in the API token subsystem. Some protect only failed verification attempts, some protect a single dashboard action, and the union limiter acts as the shared front gate.
rate_limiters.apiTokensLimiters.consumptionRateLimiter
This limiter only appears in verifyApiTokenController. The controller
consumes it when the verification request is already failing, including the
missing x-api-key branch, the missing req.ip branch, invalid privilege
schema input, and any failed verifyApiKey result.
On successful verification, the controller resets this limiter for three keys:
${req.ip}_verify, the provided raw apiKey, and the resolved
providedIpAddress. That makes it a failure-only limiter for
Verifying Tokens.
rate_limiters.apiTokensLimiters.generalUnionLimiter
This limiter is the shared burst-plus-slow gate for API token routes. It is
always used for token creation and token management, and it is only used for
verification when apiTokens.rateLimitOnSuccessfulRequest is true.
Most API token routes key this limiter by req.ip!. Token listing uses the
dedicated key ${req.ip}_list-metadata, and verification uses
${req.ip}_verify. Successful creation, revocation, metadata, rotation, IP
updates, and privilege updates call resetApiUnionLimiters(req.ip!), but
token listing and verification do not reset this limiter.
See Creating Tokens, Verifying Tokens, Revocation, Rotation, IP Restriction Updates, Privileges, Extensive Metadata, and Token Listing.
resetApiUnionLimiters() only deletes the burstLimiter and slowLimiter
keys inside generalUnionLimiter. It does not reset the operation-specific
limiters or the verification failure limiter.rate_limiters.apiTokensLimiters.operationRateLimits.newTokenCreationLimiter
This limiter protects POST /api/manage/new-token with the composite key
${req.ip}_${userId}. It is the dedicated quota bucket for token creation and
is not reset on success, so it always relies on its own duration window.
See Creating Tokens.
rate_limiters.apiTokensLimiters.operationRateLimits.revokeTokensLimiter
This limiter protects POST /api/manage/revoke with the composite key
${req.ip}_${userId}. It only applies after schema validation succeeds and the
request has already passed generalUnionLimiter.
See Revocation.
rate_limiters.apiTokensLimiters.operationRateLimits.getMetadataTokenLimiter
This limiter protects POST /api/manage/metadata with the composite key
${req.ip}_${userId}. It is the dedicated high-throughput bucket for metadata
reads, which is why its defaults are much looser than the other management
operation limiters.
See Extensive Metadata.
rate_limiters.apiTokensLimiters.operationRateLimits.rotationRateLimiter
This limiter protects POST /api/manage/rotate with the composite key
${req.ip}_${userId}. It sits in front of a destructive replace action, so its
default block duration is longer than the IP and privilege update buckets.
See Rotation.
rate_limiters.apiTokensLimiters.operationRateLimits.ipRestrictionUpdate
This limiter protects POST /api/manage/ip-restriction-update with the
composite key ${req.ip}_${userId}. It is dedicated to network whitelist
changes and uses a shorter block window than revoke or rotate.
rate_limiters.apiTokensLimiters.operationRateLimits.privilegeUpdate
This limiter protects POST /api/manage/privilege-update with the composite
key ${req.ip}_${userId}. It is dedicated to privilege changes and uses the
same default timings as the IP restriction update bucket.
See Privileges.
Configuration reference
All API token limiter settings live under apiTokens and
rate_limiters.apiTokensLimiters in the object passed to configuration().
Verification switch
Verification has one related switch outside the rate_limiters object:
Verification success switch
- Option:
apiTokens.rateLimitOnSuccessfulRequest - Description: Enables
generalUnionLimiterinverifyApiTokenControllerbefore any token validation happens. When it staysfalse, verification only usesconsumptionRateLimiter. - Default:
false
Rate limiter defaults
These are the built-in defaults from buildLimiters() when you do not provide
custom limiter values in configuration.
Verification failure limiter
- Limiter name:
rate_limiters.apiTokensLimiters.consumptionRateLimiter - Description: Verification failure limiter. It consumes points only in failed verification branches.
- Default behavior: Allows 10 failed verification attempts per minute, then blocks for 1 hour.
Shared union limiter
- Limiter name:
rate_limiters.apiTokensLimiters.generalUnionLimiter - Description: Shared burst-plus-slow front gate for creation and management, and optional gate for successful verification.
- Default behavior: The burst limiter allows 1 request per second, then blocks for 15 minutes. The slow limiter allows 50 requests per minute, then blocks for 1 hour.
Token creation limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.newTokenCreationLimiter - Description: Dedicated limiter for
POST /api/manage/new-token. - Default behavior: Allows 5 token creations per 10 minutes, then blocks for 1 hour.
Revocation limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.revokeTokensLimiter - Description: Dedicated limiter for
POST /api/manage/revoke. - Default behavior: Allows 5 revocations per 10 minutes, then blocks for 2 hours.
Metadata limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.getMetadataTokenLimiter - Description: Dedicated limiter for
POST /api/manage/metadata. - Default behavior: Allows 20 metadata requests per 2 seconds, then blocks for 30 minutes.
Rotation limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.rotationRateLimiter - Description: Dedicated limiter for
POST /api/manage/rotate. - Default behavior: Allows 5 rotations per 10 minutes, then blocks for 2 hours.
IP restriction update limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.ipRestrictionUpdate - Description: Dedicated limiter for
POST /api/manage/ip-restriction-update. - Default behavior: Allows 5 IP restriction updates per 10 minutes, then blocks for 30 minutes.
Privilege update limiter
- Limiter name:
rate_limiters.apiTokensLimiters.operationRateLimits.privilegeUpdate - Description: Dedicated limiter for
POST /api/manage/privilege-update. - Default behavior: Allows 5 privilege updates per 10 minutes, then blocks for 30 minutes.