Rate Limits
Fixed-window limits, enforced at the edge before any Firestore read happens.
| Scope | Limit | Window | Keyed by |
|---|---|---|---|
| All requests (backstop) | 120 | 60 seconds | Caller IP |
POST /v1/otp/send | 5 | 60 seconds | Application Client ID |
POST /v1/otp/verify | 10 | 60 seconds | Application Client ID |
| Sandbox: create echo bin | 20 | 1 hour | Caller IP |
| Sandbox: deliver to echo bin | 30 | 60 seconds | Caller IP |
What happens when you're limited
A rate-limited request returns 429 with:
{
"error": {
"message": "Too many OTP requests — retry in 42s",
"code": "RATE_LIMITED"
}
}
Design notes
Limits use fixed windows rather than a sliding/token-bucket algorithm — simpler to reason about, at the cost of allowing a short burst right at a window boundary. In practice this trades a small amount of burst tolerance for an implementation with no cross-request coordination beyond a single KV read and write per call.
The per-application OTP limits are intentionally tighter than the global backstop — they exist specifically to blunt automated OTP-spam against a single application, independent of how many different IPs the traffic comes from.
Verification attempts
Separate from rate limiting: each individual OTP request allows a maximum of 5 verification attempts before it's marked failed, regardless of how much of the per-minute verify limit remains.