Skip to main content
Rate limits sit on the endpoints an attacker can reach without a credential — the ones that guess passwords, enumerate addresses or send mail on somebody else’s behalf. Counting is a fixed window per client IP, except boot, which is additionally counted per publishable key.

Being limited

429
Retry-After carries the window in seconds. Wait it out; retrying sooner only consumes the next window.

The limits

Staff authentication

Invitations

Customer plane

The per-key limit on boot exists because the publishable key is the tenant boundary a flood arrives through: bots and landing-page traffic would otherwise mint visitor rows forever.

The contact’s own surface

Per session, not per IP. Behind carrier NAT one address is thousands of people, so an IP bucket on a signed-in surface throttles a crowd for what one of them did — and the session is the thing actually spending the work. /v1/contact/token is why the number exists at all: every call is a key decrypt and a signature.
The rest of the authenticated surface — the panel’s session, the organization API key — is not throttled. The credential is the gate, and it is revocable.

Failing open, failing closed

Two postures, chosen per endpoint. The counters live in a store shared by every instance of the API. When that store cannot be reached, each instance counts on its own instead — the limits stay, and they get looser rather than disappearing. That is deliberate, and it is the one place this platform does not degrade gracefully. Everything else optional turns off without its dependency; a public sign-up behind no limiter at all is the whole security posture silently off, at exactly the moment an attacker is most likely to be the reason. What you may notice while it lasts: a limit briefly allowing more than the number above, because several instances are each counting to it. Nothing answers differently, and there is nothing to handle.

Staying under them

1

Retry on 429, with backoff

Honour Retry-After when it is present; back off exponentially when it is not.
2

Do not retry a 4xx that is not 429

A 400 or a 403 will answer the same way every time.
3

Debounce boot

One boot per page load, not one per component that wants the contact.
4

Rate-limit your own resend buttons

Verification and magic-link resends are 5 an hour. A button with no cooldown burns them in seconds and the user sees nothing but a failure.