# Rate limits

The Public API rate-limits its credential endpoints (the OAuth token and
revocation endpoints) and MCP tool calls. The limits absorb retry storms and
credential-guessing; a client that caches its tokens never sees a 429.

## OAuth endpoints

| Endpoint | Counted per | Limit |
|---|---|---|
| `POST /api/v1/auth/token` | client IP **and** `client_id` (two separate buckets) | 10 requests / 60 s each |
| `POST /api/v1/oauth/revoke` | client IP | 10 requests / 60 s |

The window is sliding: each request is timestamped, and requests older than
60 seconds fall out of the bucket. On the token endpoint the per-`client_id`
check runs before the grant is exchanged, so a 429 never consumes an
authorization code or refresh token. Retrying after the window is always safe.

## Response headers

Both endpoints report the state of the per-IP bucket on their responses. The
headers are set before authentication, so they appear on auth failures too:

| Header | Meaning |
|---|---|
| `X-Rate-Limit-Remaining` | Requests left in the current 60 s window |
| `X-Rate-Limit-Reset` | Unix timestamp (**seconds**) when the window resets |

Both headers are CORS-exposed, so browser-based clients can read them.

## The 429 response

A breached limit returns HTTP 429 with an error body in the OAuth RFC 6749
shape, not the regular error envelope described in [Errors](/guides/errors):

```json
{
  "error": "invalid_client",
  "error_description": "Rate limit exceeded. Try again later."
}
```

## Backing off

- **Cache tokens.** Access tokens live for 3600 seconds. Request one and
  reuse it until shortly before expiry. A client that requests a fresh token
  per API call hits the 10-per-minute limit immediately.
- **Serialize refreshes.** Keep one in-flight token request per `client_id`
  and have concurrent callers wait on its result.
- **Honor the reset header.** On a 429, wait until the time in
  `X-Rate-Limit-Reset` before retrying; it is at most 60 seconds away. Where
  headers aren't available, use exponential backoff with jitter, starting at
  a few seconds.

## Resource endpoints

Resource endpoints (`/api/v1/customers`, `/api/v1/invoices`, …) do not
currently enforce a fixed request quota. The authoritative view for your key
is `GET /api/v1/api-key`: its `rateLimitTiers` field lists the tiers in
effect (today a single `unlimited` tier). Build clients defensively anyway:
treat any 429 as retryable and honor the headers above.

## MCP tool calls

Calls to the [MCP server](/guides/mcp) are limited separately, per
(OAuth grant, tool), with per-minute caps and per-hour caps on some tools. A
breached cap fails the tool call with an MCP protocol error rather than an
HTTP 429; the error states how many seconds to wait before retrying.
