Skip to content

Rate Limits

logi applies rate limits in two layers:

  1. Cloudflare edge — IP-based, blocking requests before they reach the app (active in deployed environments).
  2. Rails server (rack-attack) — keyed by a specific identifier (client_id, user_id, or IP), returning a JSON 429.

The Rails layer uses Rails.cache (SolidCache in production) as its store, so it works without separate Redis infrastructure.

Per-endpoint limits

EndpointLimitKeyLayerNotes
POST /session (login)10 / 3minIPRailsBrute-force defense
POST /oauth/authorize30 / minIPRails + Cloudflare
POST /oauth/token20 / minclient_idRailsFalls back to IP if client_id is missing
POST /api/v1/me/otp/*10 / minsession/userRailsDefends against SMS cost spikes
POST /api/v1/me/passkeys/*20 / minsession/userRails
POST /api/v1/devices10 / minIPRailsDevice-bootstrap brute-force defense
POST /api/v1/users/:sub/identity_verified100 / hourclient_id (Bearer hash)RailsPer reporter app
Other /api/*60 / minIPRailsGlobal fallback
Static/Health (/up, /healthz)Unlimitedsafelist

Response when exceeded

http
HTTP/2 429 Too Many Requests
Content-Type: application/json

{"error":"rate_limited"}

Some endpoints may include a Retry-After header.

Client recommendations

  • Exponential backoff: on a 429, prefer the Retry-After header; if it's absent, retry with widening intervals of 1 → 2 → 4 → 8 seconds.
  • You'll rarely hit these limits in normal flows — persistent 429s suggest an implementation bug (a retry loop).
  • For workloads where bursts are expected, consult us in advance (per-app custom limits are planned for Phase 2).

Implementation details

The server-side implementation lives in server/config/initializers/rack_attack.rb. When a limit changes, update both that file and this document together.

In the test environment, Rails.cache is null_store, so throttling is disabled. To test the rate limiting itself, swap in a MemoryStore as in spec/requests/rate_limit_spec.rb.

Identity가 제품의 신뢰를 만듭니다.