Skip to main content

Rate Limits & Security

Rate limits

Every route is throttled per minute, keyed per credential/caller. Design your integration to back off and retry rather than hammer a limited route — these are current defaults and may be tuned over time.

RouteDefault limit
Token exchange (either credential kind — shared budget)10/min
/identity/link10/min
/duty60/min
/lookup/*, /warrant-check, /bolo-check, /civilian, /vehicle60/min
/bolo20/min
/bolo/:boloId/resolve20/min
/citation, /arrest, /civilian/claim10/min
/panic6/min
/91120/min
/911/self-assign20/min
/location600/min
/waypoint300/min
/mdt-handoff10/min
/presence10/min

The token exchange endpoint has one combined budget per caller regardless of which credential kind you're using — they're two paths to the same underlying exchange, not two independent allowances.

/duty has a second, separate limit on top of its 60/min above: new Kestrel accounts created via its auto-provision path are capped at 20/hour per credential (FIVEM_AUTO_PROVISION_RATE_LIMIT_PER_HOUR) — spam protection against a flood of brand-new accounts, not a general request budget. An already-provisioned player calling /duty repeatedly never touches this cap; it only counts genuinely new account creation.

Security notes worth knowing before you build against this

  • Revocation is passive. A revoked credential's already-issued token keeps working until it naturally expires (bounded by the 12-minute TTL) — there's no instant kill switch on an in-flight token. Rotate promptly if you suspect a leak.
  • Every integration-authenticated write is audit-logged against the credential's own identity — never anonymized.
  • An integration credential is scoped to your organisation only. There's no cross-tenant path even if a token leaked.
  • Treat credentials like a password. Kestrel's own design assumes a credential will eventually leak, and limits blast radius accordingly (short-lived tokens, independent per-credential revocation, audit trails) rather than assuming it can't happen. Never commit a clientId/clientSecret to source control.
  • scopes is only partially enforced on the FiveM Bridge. Three routes actually check it: POST /citation (needs fivem.citation.issue), POST /arrest (needs fivem.arrest.log), and POST /bolo (needs fivem.bolo.trigger) — a fivem-kind credential missing the matching scope gets a clean 403 from these three. Every other FiveM Bridge endpoint (duty, lookups, warrant-check, bolo-check, identity-link, mdt-handoff, location, waypoint, presence, 911) ignores the scopes claim entirely and is reachable by any valid fivem-kind credential regardless of what's configured on it. Don't rely on FiveM Bridge scopes as a permission boundary outside those three routes. This is different for the API credential — there, scopes are real, validated at creation, and enforced on every request.