> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tallwatch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits

> Quotas for the REST API and MCP. Product traffic is not throttled.

Rate limits cap how many requests a client can send in a minute. They keep one client from saturating the API so other traffic still lands. Heartbeats and public status-page reads are not limited: ingest and public pages must keep working even if a script is over quota.

You hit a cap when a script, MCP agent, or scanner loops too fast, not when Tallwatch is checking your monitors. Writes are tighter than reads because they change data. Over a cap: `429` with `Retry-After`, a `Link` to this page, and:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "code": "rate_limited",
  "message": "Too many requests. Wait and try again.",
  "statusCode": 429,
  "retry_after_sec": 12,
  "docs_url": "https://docs.tallwatch.com/api/rate-limits"
}
```

| Header                | Meaning                                                  |
| --------------------- | -------------------------------------------------------- |
| `RateLimit-Limit`     | Sustained quota in the current window                    |
| `RateLimit-Remaining` | Tokens left (burst bucket)                               |
| `RateLimit-Reset`     | `0` under quota; seconds to wait after a `429`           |
| `RateLimit-Policy`    | `limit;w=window_seconds`                                 |
| `Retry-After`         | On `429` only                                            |
| `Link`                | On `429` only. `rel="describedby"` pointing at this page |

`RateLimit-Limit` is the sustained quota for the window. `RateLimit-Remaining` is burst tokens left, so a short spike can succeed before the sustained cap binds. `RateLimit-Reset` is `0` while you are under quota. After a `429`, `RateLimit-Reset` and `Retry-After` are the seconds to wait.

Counters are per api process.

## What is limited

Identity is either the API key or the client IP. A Bearer key has its own read and write budgets. Dashboard cookies, probers, and anything that is not a key share an IP budget.

| Who                                                         | Sustained     | Burst | Identity  |
| ----------------------------------------------------------- | ------------- | ----- | --------- |
| Dashboard, prober `/jobs`+`/results`, other IP traffic      | **600 / min** | 200   | Client IP |
| API key / MCP **reads** (`GET`, `HEAD`)                     | **300 / min** | 40    | API key   |
| API key / MCP **writes** (`POST`, `PUT`, `PATCH`, `DELETE`) | **60 / min**  | 10    | API key   |

MCP uses the public REST API with your [API key](/api/api-keys), so it shares the key budgets. `list_*` / `get_*` are reads. Create, pause, ack, resolve, and test-check are writes. An agent that calls `get_monitor` in a loop spends the same 300 reads / min as curl.

A Bearer request counts against the key bucket. Session-cookie dashboard traffic uses the IP bucket only. The two do not share a budget. Unmatched paths (including wrong-method scans) count against the IP bucket.

## Abuse-only routes

Tighter limits on expensive or guessable endpoints. Test-check, password unlock, subscribe, contact, and auth are the usual brute-force or cost targets.

| Route                             | Budget                                                                 | Identity    |
| --------------------------------- | ---------------------------------------------------------------------- | ----------- |
| `POST /api/v1/monitors/test`      | 20 / min                                                               | User or key |
| `POST /api/v1/channels/:id/test`  | 10 / min                                                               | User        |
| Status-page password unlock       | 5 / min                                                                | IP          |
| Status-page email subscribe       | 10 / min                                                               | IP          |
| Marketing contact form            | 10 / min                                                               | IP          |
| Sign-in / sign-up (`/api/auth/*`) | 100 / min per IP; magic-link and email signup 3 / min; social 10 / min | IP          |

## Never rate-limited

These stay available even when another client is over quota.

* Heartbeat ingest `GET|POST /api/v1/heartbeats/:token`
* Public status-page reads (page, RSS, badge, by-host, verify)
* Stripe webhooks, Slack acknowledge, Caddy TLS ask, `GET /health`, `GET /`

## Handling `429`

Wait `Retry-After` seconds (same value as `retry_after_sec`), then retry. Do not tight-loop: retries that ignore the header spend the burst bucket again and stay at `429`. `docs_url` is always this page. For MCP, prefer one `list_monitors` over many `get_monitor` calls in the same turn.

## Related

Create and scope keys on [API keys](/api/api-keys). Agent setup is on [MCP](/integrations/mcp).
