x-api-key or, if no API key is sent, your client IP. Each scope keeps an independent counter with a 60-second sliding window.
Identifier and exemptions
The middleware picks a rate-limit identifier in this order in this order:x-api-keyheader → key-scoped counter.X-Forwarded-Forfirst hop → IP-scoped counter.REMOTE_ADDR→ IP-scoped counter.
- The internal
DIDIT_WEBAPP_API_KEY(used only by the Didit console). - Endpoints protected by the
IsValidSessionpermission (the embedded/SDK session traffic). GET /system/healthcheck(served byHealthMiddlewarebefore this stack).- Provider webhooks: Stripe, AML watcher, face verification callbacks, KYC list mutation.
Global limits
Every authenticated request is also bucketed by HTTP method into a generic scope, so any new route inherits a sane default:
Endpoint-specific limits (next section) apply in addition to the global limit. The first scope to exceed its counter is the one that returns
429.
Endpoint-specific limits
The middleware defines stricter scopes for the highest-impact routes:GET /v3/session/<id>/decision/ is served by SessionV3RetrieveView and is not in the session-decision scope. It is governed only by the generic-get 600/min ceiling. The 100/min decision throttle applies to the v1 and v2 decision paths.429 response shape
When you exceed any scope you get an HTTP429 with this body and headers:
X-RateLimit-* headers are only emitted on the 429 response — successful responses do not include them today. Use the Retry-After value (or X-RateLimit-Reset - now) as your delay before the next attempt.
Client guidance
- Respect
Retry-Afteras the floor for your backoff. On top of it, layer exponential backoff with jitter (5s → 10s → 20s → 40s ± random) so two clients that hit429at the same time do not synchronise. - Stop retrying after a small ceiling (e.g. 5 attempts). Surface the error to your caller and alert your team — sustained 429s usually mean a runaway loop, not a transient spike.
- Use one identifier per workload. Sharing an
x-api-keyacross many backends collapses them into the same counter, which can cause one noisy neighbour to starve the rest. Issue a separate application (and therefore a separateapi_key) per environment, brand, or customer — see Programmatic registration. - Cache decision reads. The
/decision/endpoints are not designed for tight polling loops; if you need real-time updates, use Webhooks and only callGET /v3/session/<id>/decision/for back-fill or audit. - Generate PDFs offline. PDF rendering is CPU-bound (50/min cap). Trigger it from a queue after the session reaches a terminal status, not on every page view.
Sample 429-aware client
Need more headroom?
If 600 sessions per minute is not enough — for example, a marketing launch or a backfill — email support@didit.me with yourapplication UUID and the expected sustained and burst rates. Overrides are configured per application.