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

# Rate limits

> Limits, response headers, and how to stay under them.

Limits are applied **per API key**.

| Method                               | Limit                                 |
| ------------------------------------ | ------------------------------------- |
| `GET` (reads)                        | 1000 / minute                         |
| `POST` / `PATCH` / `DELETE` (writes) | 60 / minute                           |
| Burst                                | 2× the above, over a 10-second window |

If you need higher limits, contact us with your workspace id and the use case.

## Response headers

Every response - success or error - carries the current state:

```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1746489660
```

* **`X-RateLimit-Limit`** - the cap for the method class on this key.
* **`X-RateLimit-Remaining`** - how many calls you have left in the current window.
* **`X-RateLimit-Reset`** - unix seconds when the window resets.

On `429`, we additionally send `Retry-After: <seconds>`.

## When you exceed the limit

```http theme={null}
HTTP/1.1 429 Too Many Requests
Retry-After: 18
Content-Type: application/json
```

```json theme={null}
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded.",
    "details": { "limit": 60, "window": "1m" },
    "request_id": "req_..."
  }
}
```

## Pacing strategy

The cheap and correct approach: **pre-pace using `X-RateLimit-Remaining`** rather than waiting for the 429.

```typescript theme={null}
const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } });

const remaining = Number(res.headers.get("X-RateLimit-Remaining") ?? "0");
const reset = Number(res.headers.get("X-RateLimit-Reset") ?? "0");

if (remaining < 5) {
  const wait = Math.max(0, reset * 1000 - Date.now());
  await new Promise((r) => setTimeout(r, wait));
}
```

## Retrying after a 429

Honour `Retry-After` literally - don't add jitter that beats it:

```typescript theme={null}
if (res.status === 429) {
  const retryAfter = Number(res.headers.get("Retry-After") ?? "1");
  await new Promise((r) => setTimeout(r, retryAfter * 1000));
  // retry the same request
}
```

If you have multiple workers sharing one key, do not retry from every worker at the same instant. Add a small random jitter on top of `Retry-After` (e.g. 0–500ms) to spread the thundering herd.

## Best practices

* **Use webhooks** instead of polling list endpoints. See [Webhooks](/api-v2/webhooks).
* **Use the cursor's `has_more` flag**, not `while (true)`, when paginating.
* **Batch writes** at your end where the data model allows (e.g. one `PATCH /companies/{id}` with five fields instead of five PATCHes).
* **Share keys at the integration level**, not per user. One key per "thing that calls us" is the sweet spot.
