Documentation
On this page

Rate limits

Sliding-window limits protect login, passwords, second factors, and OAuth. They are keyed on what a request names, before any lookup, so unknown organizations and addresses are limited exactly like real ones.

Defaults

WhatPer IPPer account
Login codes (email/start, /v1/auth/start, the OAuth email step)30 per 10 minutes5 per 15 minutes per organization (or the server) and address
Code checks, invite acceptance, password sign-in, set-password links, identity exchanges, sign-ups60 per 10 minutes(5 attempts per code; ORG_SIGNUP_DAILY_LIMIT new organizations per address per day)
Sign-up slug checks60 per minute
Passwords: sign-ins, step-ups and changes (organizations and management)locked after 5 failures per account and IP: 1 minute, doubling to 1 hourpast 20 attempts in 15 minutes, each waits 1 second; never locked
Second-factor attempts (REST and OAuth pages)60 per 15 minutes20 per 15 minutes per user, across sessions (10 failures also end a session)
OAuth token and revocation120 per minute60 per minute per client
OAuth client registration20 per hour100 per hour per organization
Action emails to outside addresses200 per hour per organization; deliveries over the limit wait
Timeseries ingest (REST and MCP)600 batches per minute per organization, 120 per minute per user
Chat posts, edits, reactions, and pins (REST and MCP)120 per minute per user
Chat typing notices30 per minute per user
Connector tool calls (MCP)120 per minute per user
Management setup, sign-in, step-up, and enrollment30 per 10 minutes600 per 10 minutes in all
Filesystem writes: new folders, uploads, renames, moves, deletions, restores (REST and MCP)300 per minute per user

Behavior

A request counts against all of its keys, or, if any of them is exhausted, against none: a refused request uses nothing up. Refused requests get 429 with a Retry-After header in whole seconds:

HTTP
429 Too Many Requests
Retry-After: 37

{ "error": { "code": "rate_limited", "message": "too many requests; try again later" } }

OAuth endpoints answer in the OAuth error format instead, and the sign-in pages show a message.

Keys come from what the request names (the organization in the path, the email address in the body, the client id in the form), never from what a database lookup found. So an attacker cannot tell, from the limits, whether an organization or address exists.

Limits are kept in memory, so each replica enforces its own. Behind a load balancer with n replicas, the effective limit is up to n times the numbers above.

Client IP

By default the client IP is the TCP peer. Behind a reverse proxy, set CLIENT_IP_HEADER to the header your proxy sets (for example x-real-ip, or x-forwarded-for, of which the last entry is used). Only set it if the proxy overwrites the header: otherwise clients can choose their own IP. Because the last entry is used, the proxy next to the server must pass X-Forwarded-For through unchanged rather than append to it; see Docker.

Within any window a key accepts at most its limit, and refused requests use up nothing.

Esc
↑ ↓ navigate↵ open