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

# Rate limits

> How many requests a team can make per minute, the headers that report what is left, and how to handle a 429.

Limits are per team. Every API key on the team, and the [MCP server](/mcp/overview) connected with
one, draws on the same budget. Requests are counted per bucket, in fixed one-minute windows.

## Limits

| Bucket | Routes | Requests per minute |
| - | - | - |
| Sequencer reads | `GET /v2/sequencer/*` | 600 |
| Sequencer writes | `POST`, `PATCH`, `PUT` and `DELETE` on `/v2/sequencer/*` | 300 |
| Heavy reads | `GET /v1/call-log` and `GET /v1/scored-calls` (the lists), `/v1/analytics/*`, `/v1/sequences` and `/v1/sequences/{id}` | 120 |
| Everything else | All other endpoints | 1200 |

Single-record reads such as `GET /v1/call-log/{id}` and `GET /v1/scored-calls/{id}` count toward
everything else, not heavy reads.

These are the current limits. Salesfinity gives notice in the [changelog](/changelog) before
lowering any of them.

## How requests are counted

* **Windows start on the minute.** A window runs from one clock minute to the next, not for 60
  seconds after your first request, and the count starts again at zero when it closes.
* **Each bucket counts on its own.** A team at its heavy-reads limit can still make Sequencer calls
  and everything else.
* **A refused request still counts.** Retrying before the window closes is refused again, so wait
  for `Retry-After` rather than retrying in a loop.
* **Every authenticated request counts, whatever its outcome.** A **400**, a **404** for a record
  outside your team, and a Sequencer write answered from its `Idempotency-Key` all count.
* **Only authenticated requests count.** A request without a valid API key gets **403** and is not
  counted, and neither is a request to a path that matches no route.

## Response headers

Every response to an authenticated request carries three headers for the bucket it counted
against:

| Header | Meaning |
| - | - |
| `X-RateLimit-Limit` | Requests allowed per minute in this bucket |
| `X-RateLimit-Remaining` | Requests left in the current window |
| `X-RateLimit-Reset` | Seconds until the window closes, from 1 to 60 |

Treat the headers as optional. On the rare occasion a request cannot be counted, it goes through
without them rather than failing.

## When you hit a limit

Over a limit, the request is refused with **429 Too Many Requests**. The `Retry-After` header gives
the seconds until the window closes, from 1 to 60, and the body uses the usual
[error envelope](/api-reference/errors):

```json theme={null}
{
  "message": "Rate limit of 300 requests per minute exceeded. Retry in 12s.",
  "error": "Too Many Requests",
  "statusCode": 429,
  "request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}
```

A 429 is returned before the request is processed, so the request had no effect. Wait for
`Retry-After`, then send the same request again, including a `POST` that is otherwise not safe to
retry. The `X-RateLimit-*` headers come on the 429 as well.

## Pacing a bulk job

Rather than waiting for a 429, read `X-RateLimit-Remaining` on each response and pause for
`X-RateLimit-Reset` seconds when it reaches zero:

```js theme={null}
const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));

async function salesfinity(path, init = {}) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`https://client-api.salesfinity.co${path}`, {
      ...init,
      headers: { "x-api-key": process.env.SALESFINITY_API_KEY, ...init.headers },
    });

    if (res.status === 429) {
      await sleep(Number(res.headers.get("Retry-After") ?? 1));
      continue;
    }

    if (res.headers.get("X-RateLimit-Remaining") === "0") {
      await sleep(Number(res.headers.get("X-RateLimit-Reset") ?? 1));
    }
    return res;
  }
  throw new Error("Still rate limited after 5 attempts");
}
```

Because the budget is shared, running several workers in parallel does not raise it, and a burst
from one integration can throttle another on the same team. Give long-running syncs room by keeping
them well under the limit. Browser code can read these headers too: the API exposes them to
cross-origin scripts.

The MCP server handles this for an AI assistant: it waits out a `Retry-After` of up to 30 seconds
and sends the request once more, and reports a longer wait as an error.


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