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

# Rate limits

> The real numbers, what happens at 429, and how to pace an integration

## The limit

**120 requests per minute, per account, per surface.** The buckets are keyed to the clinic account, not to individual credentials — but the REST API and the [MCP server](/mcp/overview) each have their own: every API key on the account shares one 120/minute REST budget, and every AI connector shares a separate 120/minute MCP budget. A runaway batch job can't starve the team's assistants, and a looping assistant can't starve your integrations — but a bursty integration still spends its whole surface's allowance for the account, so pace accordingly.

The window is a plain per-minute bucket, not a per-second pace: bursts are fine, up to 120 requests in quick succession (unlike the legacy API's strict one-request-per-second throttle) — the budget just has to last the rest of the minute.

An unauthenticated request (no bearer token) is limited far more tightly — **2 requests per minute, per IP address** — so anonymous traffic can't starve the account's legitimate integrations or probe the API at volume. Legitimate unauthenticated traffic is essentially one thing: the MCP handshake's initial 401 that points a connecting client at OAuth discovery, and that fits comfortably.

Every response carries:

```
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 41
```

## When you go over

A `429` with a stable error shape:

```json theme={null}
{ "message": "Too many requests.", "code": "rate_limited" }
```

```
HTTP/1.1 429 Too Many Requests
Retry-After: 37
```

`Retry-After` is the number of seconds until the bucket has room again — wait at least that long before your next call rather than retrying immediately. See [Errors](/guides/errors#error-code-catalogue) for `rate_limited` alongside the rest of the error catalogue.

<Note>
  Dynamic client registration for MCP connectors (`POST /v3/oauth2/register`) has its own, much tighter limits — 10 requests per minute per account and IP, and 60 per minute per account — since it's unauthenticated by design (there's nothing to pre-register). It doesn't share the 120/minute budget; ordinary API and MCP calls are unaffected by it.
</Note>

## Pacing an integration

* **120/minute is two requests per second sustained** — comfortable for interactive use (a booking widget, a CRM sync reacting to webhooks), but remember it's the whole account's budget, shared with every other integration and connector on it.
* **For bulk work — imports, full exports — pace deliberately rather than bursting.** Run steadily through the minute instead of firing 120 requests in the first seconds and idling for the rest; watch `X-RateLimit-Remaining` and slow down as it approaches zero. See [Data migration → Throughput and retries](/guides/data-migration#throughput-and-retries) for the numbers on a realistic import.
* **Batch where the API lets you.** A patient with embedded `numbers[]`/`address` is one request; an invoice with all its line items is one request; `POST /line_items` with `lines: [...]` adds several lines in one call. Fewer, larger requests get more done inside the same budget than the equivalent one-record-at-a-time calls.
* **Prefer [cursor pagination](/guides/querying#cursor-pagination) for large reads.** It doesn't change your rate budget, but it keeps each page's cost constant, so a long walk doesn't get slower (and doesn't tempt you into re-fetching pages) as a dataset grows.
* **Back off on `429`, don't retry immediately.** A tight retry loop against an exhausted bucket just keeps failing until the window rolls over — honour `Retry-After`.

## Idempotency-Key interplay

An `Idempotency-Key` retry and a fresh request cost the same rate-limit token — **replaying an idempotent request still counts against the account's 120/minute**, because the rate limiter runs before the idempotency check in the request pipeline. Idempotency keys make retries *safe* (no duplicate record), not *free*. If a client is retrying the same key in a tight loop because of a slow response, it will still get throttled like any other burst; the fix is the same as for any timeout — back off, don't hammer. See [Writing → Retrying safely](/guides/writing#retrying-safely-idempotency-keys) for how idempotency keys behave on their own terms.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.