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

# Rate limits

> Where limits apply, the per-plan ceilings, and how to handle a 429.

## What is limited

| Surface                                                           | Limited                      |
| ----------------------------------------------------------------- | ---------------------------- |
| [`POST /v1/users`](/api/server/users/push-a-user)                 | No                           |
| [`/api/killswitch`](/api/server/kill-switch/read-the-kill-switch) | No                           |
| [MCP endpoints](/mcp/overview)                                    | **Yes**                      |
| [Agent artifacts](/artifacts/overview)                            | No — cached for five minutes |
| The widget's own traffic                                          | Yes, internally              |

The server API is not rate limited. It requires a secret key, which is a
credential you control, so the throttle would bound only you.

The MCP endpoints **are** limited, and that matters most on the workspace-level
endpoint, which is unauthenticated — the only credential is a workspace id in a
URL that people paste into assistants.

## The ceilings

Requests per minute, per route.

| Plan        | Per end user | Per key | Per workspace |
| ----------- | ------------ | ------- | ------------- |
| **Starter** | 60           | 300     | 300           |
| **Growth**  | 120          | 900     | 900           |
| **Scale**   | 300          | 3,000   | 3,000         |

Trial workspaces, and any plan Peeve does not recognise, get the Starter tier —
tier resolution fails closed, so a ceiling is never raised by accident.

Buckets are **per route**, and the window is a fixed 60 seconds.

For MCP specifically:

* The **workspace endpoint** buckets per workspace and per hashed source IP.
  There is no key bucket, because there is no key.
* The **per-user endpoint** buckets per workspace and per **contact** — the
  identity the grant is bound to. The token itself never goes in a bucket,
  because it is a secret.

## Handling a 429

Every rate-limited response carries a `retry-after` header, in seconds. Honour
it.

```js theme={null}
async function callPeeve(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if (res.status !== 429 || attempt >= 3) return res;

  const wait = Number(res.headers.get("retry-after") || 60) * 1000;
  await new Promise((r) => setTimeout(r, wait));
  return callPeeve(url, init, attempt + 1);
}
```

<Warning>
  Do not retry immediately on a `429`. The window is fixed, so an immediate
  retry is guaranteed to fail and only consumes the next window's allowance.
</Warning>

On MCP, a rate-limited call is a real `429` with JSON-RPC code `-32002` — not a
`200` with an error member — precisely so clients back off properly.

<Note>
  If a client is polling `tools/list` on a timer, stop. The MCP server reports
  `listChanged: false`, so the list does not push updates and re-listing on a
  loop is the usual cause of a `429`.
</Note>

## Limiters fail open

If the limiter itself is unavailable, requests are **allowed**. The limiter must
never take a customer's agent offline.

This means limits are best-effort under partial failure. It is a deliberate
trade: the alternative is a limiter outage silently disabling every customer.

## Limits run before the work

On the expensive paths the limiter runs first, so a shed request costs nothing.
The MCP endpoints check it **before** building the capability projection, which
is a multi-read query — otherwise anyone who learned a workspace id could query
your data for free.

## Credits are separate

Distinct from rate limits, a workspace consumes credits. When the pool is
exhausted the agent stops starting new conversations, while conversations
already in progress continue. See [Plans and gating](/reference/plans).
