# Rate limits

Requests per minute on each plan, the headers that report your budget, how to back off on a 429, and the platform limits that apply on top.

Pozy limits how many API requests a workspace can make per minute. The limit is shared by every API key and MCP connection in the workspace. Requests the dashboard makes have their own budget of 120 per minute for each workspace, so a busy team in the app never slows your integration down.

## Limits by plan

| Plan | Requests per minute | When it applies |
| --- | --- | --- |
| Free | 30 | No payment method on file. |
| Build | 120 | A payment method is on file. |
| Enterprise | 300 | An enterprise contract, or more than 2,000 connected accounts. |

The window is a fixed calendar minute. The count resets at the start of each minute, not 60 seconds after your first request.

If you need more than 300 requests per minute, write to support@pozy.ai with your use case.

### Busy periods

Your workspace limit is the most you can send, not a guarantee. When the platform as a whole is very busy, Pozy can answer `429` with a `Retry-After` header before your workspace reaches its own limit. The body has the same `type` and `code` as below, with a message such as `Pozy is busy right now. Retry in 5s.` Analytics endpoints have a tighter per-second budget, so spread bulk analytics reads over time. Back off the same way in every case: wait `Retry-After` seconds, then send the request again.

## Read your budget from the headers

Every response tells you where you stand:

```bash Terminal
curl -i https://api.pozy.ai/v1/accounts \
  -H "Authorization: Bearer $POZY_API_KEY"
```

```http
HTTP/2 200
x-ratelimit-limit: 120
x-ratelimit-remaining: 107
x-ratelimit-reset: 41
x-request-id: req_P3sK8vN1qL7xT2mR9cZa
```

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` | Requests allowed in the current minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | Seconds until the window resets. |
| `Retry-After` | On `429` only: seconds to wait before retrying. |

The reset value is a number of seconds, not a timestamp, so clock skew between your servers and Pozy does not matter.

## When you hit the limit

The request is refused before it runs, so nothing happened and it is always safe to send again:

```json
{
  "error": "Too many requests. Retry in 41s.",
  "type": "rate_limit_error",
  "code": "rate_limit_exceeded"
}
```

Wait for `Retry-After` seconds, then retry. A small random delay on top stops many workers from retrying in the same second.

```ts Node
async function withRetry<T>(call: () => Promise<Response>, attempts = 5): Promise<Response> {
  for (let i = 0; ; i++) {
    const res = await call();
    if (res.status !== 429 && res.status < 500) return res;
    if (i === attempts - 1) return res;
    const retryAfter = Number(res.headers.get("retry-after"));
    const base = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : 1000 * 2 ** i;
    await new Promise((r) => setTimeout(r, base + Math.random() * 500));
  }
}

const res = await withRetry(() =>
  fetch("https://api.pozy.ai/v1/accounts", {
    headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
  }),
);
console.log(await res.json());
```
```python Python
import os, random, time, requests

def with_retry(send, attempts=5):
    for i in range(attempts):
        resp = send()
        if resp.status_code != 429 and resp.status_code < 500:
            return resp
        if i == attempts - 1:
            return resp
        retry_after = resp.headers.get("retry-after")
        delay = float(retry_after) if retry_after else 2 ** i
        time.sleep(delay + random.random() * 0.5)

resp = with_retry(lambda: requests.get(
    "https://api.pozy.ai/v1/accounts",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    timeout=30,
))
print(resp.json())
```

> **Retrying writes:**
> 
> A `429` is always safe to retry. A `5xx` or a timeout on a create may have gone through. Send an `Idempotency-Key` on creates so a retry cannot make a duplicate ([idempotency](https://pozy.ai/docs/getting-started/idempotency)).

## Platform limits on top

Your Pozy budget is not the only limit. Every platform caps what one account can do, and some endpoints have quotas of their own. When one of those says stop, you get `429` with `code: "rate_limited"`, plus `Retry-After` whenever the platform said how long to wait.

To keep accounts in good standing, Pozy also caps how often one account publishes:

| Limit | Value |
| --- | --- |
| Posts per account per hour | 25 |
| Posts per account per day | Instagram 100, Facebook 100, Threads 250, X 50, Pinterest 25, TikTok 15 videos and 15 photos, every other platform 50 |

A post over a cap is refused with the cooldown in the message. Spread large batches over time with a [queue](https://pozy.ai/docs/publishing/queues) instead.

Platform rules change often. The per-platform pages under [Platforms](https://pozy.ai/docs/platforms) list the limits that matter for each one.

## Stay under the limit

- **Use webhooks instead of polling.** One `post.published` event replaces dozens of `GET /v1/posts/{postId}` calls ([webhooks](https://pozy.ai/docs/webhooks)).
- **Page with large pages.** Most list endpoints accept a `limit` far above the default ([pagination](https://pozy.ai/docs/getting-started/pagination)).
- **Upload many posts at once.** `POST /v1/posts/bulk-upload` takes a CSV instead of one request per post ([CSV import](https://pozy.ai/docs/publishing/csv-import)).
- **Sync incrementally.** `GET /v1/posts?updatedSince=...` returns only what changed since your last sync.
- **Cache what rarely changes.** Account lists, profiles and queue schedules can be cached for minutes and refreshed on `account.connected` and `account.disconnected` events.
- **Watch the headers.** Slow down when `X-RateLimit-Remaining` gets low instead of waiting for a `429`.

More keys do not give you more requests: the budget belongs to the workspace. Use separate keys for access control, not for throughput.
