Rate Limit

Throttle by IP, user, route or endpoint

Rate limiting protects your API from abuse, brute-force and accidental hammering. Nucleus ships a Redis-backed limiter with three algorithms and independent budgets for public, private and — most importantly — sensitive auth routes.

Limits can be keyed by IP, by authenticated user, by endpoint, or any combination, so you can express rules like “5 logins per minute per IP” and “100 reads per minute per user” simultaneously. Standard rate-limit headers tell clients exactly where they stand.

Strategy#

Choose the algorithm and the Redis namespace. The sliding window is the most accurate and the sensible default for most APIs.

enabledbooleanOptional

Turn the limiter on. When off, no throttling is applied.

Defaultfalse
strategy'sliding-window' | 'fixed-window' | 'token-bucket'Optional

The counting algorithm. sliding-window smooths bursts across the window boundary; fixed-window is cheapest but allows edge bursts; token-bucket permits controlled bursting up to a refill rate.

  • sliding-windowAccurate, burst-resistant. Recommended.
  • fixed-windowCheapest; can allow 2× bursts at window edges.
  • token-bucketAllows bursts up to a bucket size, refilled over time.
Default'sliding-window'
keyPrefixstringOptional

Redis key prefix for limiter counters — isolate multiple apps on one Redis.

Keying dimensions#

What a limit is counted against. Combine these to build precise rules — per-IP stops a single attacker, per-user stops a single account, per-endpoint isolates hot routes.

byIpbooleanOptional

Count requests per client IP.

byUserIdbooleanOptional

Count requests per authenticated user.

byEndpointbooleanOptional

Track each endpoint separately so a busy route doesn't exhaust another's budget.

skipSuccessfulRequestsbooleanOptional

Only count failures toward the limit. Perfect for login: legitimate success never burns budget, but repeated failures do.

Budgets#

Per-category limits. Each budget is a window (duration string) plus a max count. Auth routes get their own tighter budgets, and the most sensitive ones add a blockDuration that locks out a client after the limit is hit.

Tight login budget with lockout
1{2  "rateLimit": {3    "enabled": true,4    "strategy": "sliding-window",5    "skipSuccessfulRequests": true,6    "byIp": true,7    "authRoutes": {8      "login": { "window": "1m", "max": 5, "blockDuration": "15m" },9      "register": { "window": "1h", "max": 10 }10    },11    "privateRoutes": { "window": "1m", "max": 120 }12  }13}
authRoutesobjectOptional

Budgets for authentication endpoints: a shared default plus stricter per-route overrides. Each override is { window, max } and the sensitive ones add blockDuration — a cooldown that locks the client out after the limit trips.

windowstringOptional

Default window applied to all auth routes, e.g. "1m".

maxnumberOptional

Default max requests per window across auth routes.

login{ window?; max?; blockDuration? }Optional

Override for POST /auth/login. blockDuration locks the client out after the limit trips, e.g. "15m".

register{ window?; max?; blockDuration? }Optional

Override for POST /auth/register.

passwordReset{ window?; max?; blockDuration? }Optional

Override for the password-reset request/confirm routes.

magicLink{ window?; max?; blockDuration? }Optional

Override for magic-link issuance.

publicRoutes{ window?: string; max?: number }Optional

Budget applied to unauthenticated/public endpoints.

privateRoutes{ window?: string; max?: number }Optional

Budget applied to authenticated endpoints.

Response headers & allow/deny lists#

Control the feedback clients receive and carve out exceptions. Whitelisted clients bypass limits; blacklisted ones are always rejected.

headers{ remaining?; reset?; limit? }Optional

Custom names for the rate-limit response headers (remaining quota, reset time, total limit) so clients can back off gracefully.

whiteliststring[]Optional

Identifiers (e.g. IPs) that bypass rate limiting entirely.

blackliststring[]Optional

Identifiers that are always blocked, regardless of budget.

Under the hood — the limiter#

Every counter lives in Redis (keyed by prefix + category + auth-type + the enabled ip/user/endpoint dimensions), so limits hold across instances. The three strategies differ only in how they count.

sliding-windowtimestamp listOptional

Keeps the timestamps of recent hits and drops any older than the window on each check — a precise rolling count with no bucket-boundary bursts. The default strategy.

fixed-windowper-bucket counterOptional

Increments a counter keyed by the current window id (now / windowMs). Cheapest, but allows up to 2× max across a boundary.

token-bucketrefill rateOptional

Refills tokens at max/window and spends one per request, allowing controlled bursts up to the bucket size while smoothing the sustained rate.

lockout (blockDuration):blocked keyOptional

When an auth route with a blockDuration trips its limit, a {key}:blocked marker is written with an until timestamp; subsequent requests are refused with a retryAfter until it expires — so brute-forcing login costs the attacker a real lockout, not just a slow drip.

skipSuccessfulRequestsrefundOptional

When enabled, a request that ultimately succeeds is refunded (a timestamp popped / the counter decremented), so only failed attempts erode the budget — ideal for login where you want to punish wrong passwords, not correct ones.

allow/deny matchingglobOptional

whitelist and blacklist entries support * wildcards (compiled to a regex). Whitelisted IPs bypass entirely; blacklisted IPs are refused for 24h before any counting happens.

Related sections