Rate limits
SenseCrypt rate-limit policy for the protocol endpoints you integrate against — the per-action windows and ceilings you need to design retries, the 429 response shape with Retry-After, how the client IP is determined, and how to handle limits.
SenseCrypt applies per-action, fixed-window rate limiting to protect against brute force, enumeration, and abuse. Limits are shared across every instance — there is no per-instance in-memory state, so a limit is a limit no matter which node serves the request.
This page documents the limits on the protocol endpoints you call so you can design sensible retries. The limits on the sign-in ceremony itself (email entry, registration, CIBA dispatch, and the like) are strict and tuned to stop abuse; they aren't published, because normal user traffic stays well under them and only automated abuse trips them.
The response when you're limited
Exceeding any scope returns:
- HTTP
429 Too Many Requests - A
Retry-Afterheader with the number of seconds until the window rolls over. - A JSON body:
{
"error": "rate_limited",
"message": "Too many attempts. Please wait and try again.",
"retry_after_seconds": 42
}The retry_after_seconds value appears in the body and as the Retry-After header. On the SCIM endpoints the same 429 is re-rendered as a SCIM error envelope, with the Retry-After header preserved. Honor Retry-After — retrying before it elapses just burns another attempt.
How limits are scoped
Each action is limited independently on one or more scopes. The scopes that apply to the protocol endpoints are:
ip— the caller's IP address (see Determining the client IP).client_id— the OAuth client / M2M app.
A request must pass every scope for its action. A window is a fixed clock-aligned bucket: for a 60-second window, all requests in the same wall-clock minute share a counter.
Protocol endpoint limits
These are the limits on the OAuth / OIDC and SAML protocol endpoints. Columns are the scope, its window (seconds), and the maximum requests allowed in that window.
| Action | Scope | Window (s) | Max |
|---|---|---|---|
oidc_token | ip / client_id | 60 / 60 | 60 / 120 |
oidc_authorize | ip / client_id | 60 / 60 | 60 / 120 |
oidc_par | ip / client_id | 60 / 60 | 60 / 120 |
oidc_revoke | ip / client_id | 60 / 60 | 60 / 120 |
oidc_introspect | ip / client_id | 60 / 60 | 120 / 240 |
ciba_bc_authorize | ip / client_id | 60 / 60 | 30 / 60 |
saml_sso | ip | 60 | 60 |
Introspection is deliberately looser (resource servers introspect on a hot path and should cache). CIBA's backchannel start is tighter than the token endpoint because each call may dispatch a sign-in notification.
The sign-in ceremony (email entry, device registration, PIN and verification handling) and the admin console carry their own strict, abuse-tuned limits on top of these. They are intentionally not documented here: legitimate end-user and console traffic never approaches them, so you don't need to design around them — but an automated load test will hit them.
Determining the client IP
For IP-scoped limits, the client IP is taken from the X-Forwarded-For header, counting from the right:
- The IP is read
TRUSTED_PROXY_HOPSpositions from the right of the header (default 1, i.e. a single reverse proxy / ALB). Set it to2for CDN → ALB, or0to ignore the header and use the socket peer.
If you self-host, set TRUSTED_PROXY_HOPS to match your proxy topology — too high and clients can spoof their IP; too low and every client can appear to share one IP (which would trip IP-scoped limits for everyone). See Self-hosting.
Handling limits in your integration
- Cache introspection results. Don't introspect the same token on every request — verify the JWT locally against the JWKS and reserve introspection for revocation checks.
- Back off on
429. ReadRetry-Afterand wait; don't hammer. - Don't share one egress IP across unrelated tenants at high volume without accounting for the IP-scoped ceilings on the protocol endpoints.
- Expect a tight sign-in funnel. The sign-in and registration limits are strict by design (they're anti-abuse). Normal user traffic stays well under them; automated load tests will not.
Related
- Error codes — the full error taxonomy, including the
429shape. - Self-hosting — proxy configuration and the
TRUSTED_PROXY_HOPSknob. - Troubleshooting — what to do when you hit a limit unexpectedly.
Error codes
The complete SenseCrypt error taxonomy — OAuth/OIDC, SAML, SCIM, and device-plane error codes, their HTTP statuses, the exact envelopes, and what each one means.
CORS
How cross-origin browser access to SenseCrypt works — the backend registers no CORS middleware of its own, so CORS is terminated at the operator's reverse proxy, and confidential apps should prefer server-side token exchange.