# Quotas and rate limits

The free beta's limits. Months are UTC calendar months.

| Limit | Value | Over the limit |
|---|---|---|
| Sends per account per month | 10,000 (the account's `monthly_limit`) | `429 quota_exceeded`, `Retry-After` until the next month |
| Sends per token per minute | 30 | `429 rate_limited`, `Retry-After` until the next minute |
| Sends per account per minute, across all its tokens | 60 | `429 rate_limited` |
| Devices per account | 20 | `409 limit_reached` when registering or approving a link |
| Send tokens per account | 50 | `409 limit_reached` |
| Link approvals per account | 5 in any 10 minutes | `409 limit_reached` |
| Link requests per client IP | 100 per 10 minutes | `429 rate_limited` |
| Wrong link codes per account | 5 in any 10 minutes | `429 link_locked` |
| Wrong link codes per client IP | 50 per 10 minutes | `429 link_locked` |
| New accounts per client IP | 100 per hour | `429 rate_limited` |
| New accounts, service-wide | 500 per UTC day | `503 service_unavailable` |
| Request size | 16 KB | `413 payload_too_large` |

- **One send counts once**, however many devices it reaches.
- **Merged accounts share one quota**: after a merge, every token's sends count against the
  surviving account.
- **Per-IP limits are flood guards**, sized so that a whole office or conference behind one
  address isn't blocked.
- The operator can pause or cap sends service-wide; sends then get
  `503 service_unavailable` with `Retry-After`.

## Headers

Every response to a send after the token is known, errors included, carries:

| Header | Meaning |
|---|---|
| `X-RateLimit-Limit` | The account's monthly send limit |
| `X-RateLimit-Remaining` | Sends left this month |
| `X-RateLimit-Reset` | When the monthly counter resets, in epoch seconds (the start of the next UTC month) |
| `Retry-After` | On 429 and 503: seconds to wait before retrying |

## Usage

The app shows "1,234 / 10,000 this month" from `GET /v1/usage` (account key or device
credential), which returns `{month, used, limit, remaining, resets_at}`.
