Skip to content

Application Rate Limiting

Rate limiting is enforced in Django REST Framework, backed by the configured Redis cache. It does not depend on the external gateway service, so the same limits apply in every deployment topology.

Default Scopes

Scope Identity Rate Applies to Rationale
public_read Client IP 120/min Unauthenticated safe methods Allows mobile bootstrap and public lookup traffic while limiting anonymous scraping bursts.
public_write Client IP 20/min Unauthenticated unsafe methods Protects invite, registration, login, survey, webhook, and Turnstile-gated flows, which are the highest abuse risk seen in Sentry-style repeated failure patterns.
authenticated_read User ID 600/min Authenticated safe methods Read-heavy authenticated screens can make frequent list/detail requests, so this is intentionally higher.
authenticated_write User ID 120/min Authenticated unsafe methods Allows normal operator workflows while limiting accidental loops and scripted write abuse.

Endpoint-Specific Scopes

Scope Identity Rate Applies to Rationale
logout_ip Client IP 20/min POST /user/v1/logout/ The refresh token authorizes revocation, so logout accepts callers without a valid access token and needs its own IP-based limit.
validate_cooling_user_id_ip Client IP 30/min POST /v1/users/validate-cooling-user-id/ Keeps the existing targeted protection for ID enumeration attempts.
analytics_api_token API token ID 100/min External analytics API Partner integrations are keyed by token rather than user session or IP because many legitimate clients run from shared infrastructure.

Trusted Proxies And Client IPs

IP-based throttles use Django REST Framework's NUM_PROXIES setting. The deployment default is 2, matching the production chain Cloudflare -> Caddy -> web. Set the NUM_PROXIES environment variable to the actual number of trusted proxy hops in front of Django. An incorrect value can group unrelated callers into one throttle bucket or allow callers to influence the address selected from X-Forwarded-For.

Response Shape

When a request is throttled, DRF returns HTTP 429 with a Retry-After header. The application exception handler normalizes the body:

{
  "error": {
    "code": "rate_limited",
    "message": "Request was throttled.",
    "retry_after": 60
  }
}

The rates should be tuned from production telemetry. Repeated validation, registration, login, invite, and other unauthenticated failures should push public_write lower or toward endpoint-specific scopes. Legitimate mobile client bursts should be evaluated before reducing authenticated read/write limits.