Plan-aware authenticated throughput is implemented in current repository source. A managed deployment must be verified separately; source completion is not evidence that a hosted rollout has occurred.
Default Limits
A route-specific Fastify
config.rateLimit replaces the 5,000 requests/minute Fastify default for that route; those two Fastify policies are not stacked. The Redis-backed standard developer plan budget remains an additional policy when standard API-key authentication applies.
Commerce, the SCIM Bearer data-plane routes under /scim/v2/*, admin, and other custom-auth routes remain outside the standard developer plan bucket. Their active Fastify policy still applies. The standard API-key-authenticated /v1/scim/tokens management routes do consume the plan bucket.
Response Headers
Protected responses include rate-limit headers so your application can track the policy represented by that response. Successful standard developer API-key responses report the developer’s plan budget; a request rejected earlier by the active Fastify default or route policy reports that policy instead. Treat the active Fastify policy and Redis plan policy as separate applicable controls, and always honor a429 plus Retry-After.
429 Error Response
When you exceed a rate limit, the API returns a429 Too Many Requests status with the following body:
Retry-After header tells you the minimum number of seconds to wait. Generic IP/route-limit responses are produced by @fastify/rate-limit and can use a different error code/message; clients should branch on HTTP status 429 and honor the header rather than matching message text.
Authenticated Limiter Availability
The standard developer API-key plan limiter fails closed when its Redis transaction cannot be completed:503 Service Unavailable, not 429. Retry it as a transient service failure; do not treat it as proof that the request reached the protected handler.
Reading Rate Limits from SDKs
All three SDKs automatically parse rate limit headers from every response. You can read them viaclient.lastRateLimit (TypeScript/Python) or client.LastRateLimit() (Go).
After a Successful Call
Handling 429 Errors
When a429 is returned, the error object includes rate limit info with the retryAfter value:
Retry Strategy
Use exponential backoff with jitter to avoid thundering-herd problems when multiple clients hit the limit simultaneously.Best Practices
The JWKS endpoint (
/.well-known/jwks.json) is exempt from rate limits. Local
JWKS-based verification avoids the POST /v1/tokens/verify rate limit when a
revocation lookup is not required, but current standalone helpers still make a
JWKS network request per call.- Cache tokens — Grant tokens are valid JWTs. Store and reuse them until they expire instead of requesting new ones per operation.
- Use local verification deliberately —
verifyGrantToken()validates with JWKS and avoids the online verification rate limit, but the standalone helper fetches JWKS on each call. - Use webhooks instead of polling — Subscribe to webhook events like
grant.createdandgrant.revokedrather than polling grant or audit endpoints. - Honor
Retry-After— When you receive a429, always use theRetry-Afterheader value as your minimum wait time. - Spread requests — If your system makes burst requests (e.g., batch token exchanges), add short delays between calls.
Self-Hosted Deployments
If you’re running the Grantex auth service yourself, rate limits are configurable. The default Fastify IP policy is set inapps/auth-service/src/server.ts; a route-specific Fastify policy lives beside its route and replaces that default on the route. Standard developer API-key budgets and their window are defined in apps/auth-service/src/plugins/dynamicRateLimit.ts and remain additional. All instances must share Redis for standard developer budgets to remain global across the deployment. Fastify IP counters are process-local unless you configure a shared store or equivalent ingress enforcement.
See the Self-Hosting guide for deployment instructions.