Raven Docs

Rate Limits

Keyed by identity when one exists, IP only when it doesn't.

Rate limits are a fixed-window counter, applied to a handful of routes with real abuse potential:

RouteLimit
Registration5 / window
Login10 / window
API key creation20 / window
RTC token creation60 / window
Telemetry ingest600 / window

Window size defaults to 60 seconds.

What the budget is keyed on

The most specific identity a request actually carries, checked in this order:

  1. The API key's own public id, when a request is authenticated with one. Keyed on the key itself, not its project — two keys on one project don't share a budget, so a noisy or compromised key can't spend its sibling's headroom.
  2. The signed-in user's id, for dashboard-session routes.
  3. Client IP, only when neither exists — registration and login have no identity yet to key on.

Identity is never combined with IP once one is available. An authenticated abuser rotating IPs is still one identity and stays capped as one; folding IP back in would only reopen the problem identity-based keying exists to fix — every legitimate user behind one corporate NAT sharing a single bucket.

Reading a 429

{
  "code": "RAVEN_RATE_LIMITED",
  "message": "Too many requests — please try again later",
  "retryAfterSeconds": 42,
  "requestId": "req_9f2c41ab77e0c3d5b1a4e8f2"
}

retryAfterSeconds is read from the limiter's own remaining window — use it rather than a fixed backoff.

Chat and signaling have their own limiters

Chat's rate limits (send, react, type, subscribe, connect) are separate and already keyed per-subject — a chat token always carries an identity by the time a limited action happens, so it never had the IP-only problem. Signaling's WebSocket-upgrade limiter is IP-only by necessity: it guards the connection attempt itself, before any token is verified — the same situation login and registration are in.

See WebSocket Protocol for the close code a chat-side limit produces.