# Rate Limits (/docs/api/rate-limits)



Limits [#limits]

Each API key has a budget of requests per minute, set by your organization's plan:

| Plan | Requests per minute |
| ---- | ------------------- |
| Free | 60                  |
| Paid | 600                 |

The window is a fixed calendar minute. Limits apply per API key, so two keys each have their own budget. Subscriptions from before the current pricing keep their limit; `GET /api/v1/me` shows the limit of your key.

[MCP](/docs/mcp) tool calls count against the same limits: per API key, or, for assistants signed in with OAuth, per connected app and user.

Response headers [#response-headers]

Every response carries the current state:

| Header                  | Description                              |
| ----------------------- | ---------------------------------------- |
| `X-RateLimit-Limit`     | Requests allowed per minute              |
| `X-RateLimit-Remaining` | Requests left in the current minute      |
| `X-RateLimit-Reset`     | Unix time (seconds) when the minute ends |

When you hit the limit [#when-you-hit-the-limit]

The API answers `429` with a `Retry-After` header in seconds:

```json
{
    "error": {
        "type": "rate_limit_error",
        "code": "rate_limited",
        "message": "Too many requests, retry after the time in Retry-After",
        "docUrl": "https://feedhorizon.com/docs/api/errors#rate_limit_error",
        "requestId": "req_m2x81a0kfd3h9s"
    }
}
```

```javascript title="Node.js"
async function apiCall(url, options, maxRetries = 3) {
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        const res = await fetch(url, options);
        if (res.status !== 429) return res;
        const wait = Number(res.headers.get('Retry-After') ?? 60);
        await new Promise((r) => setTimeout(r, wait * 1000));
    }
    throw new Error('Rate limit: retries exhausted');
}
```

Tips [#tips]

1. Slow down when `X-RateLimit-Remaining` gets low instead of waiting for a 429.
2. Use [webhooks](/docs/guides/webhooks) instead of polling post status.
3. Send writes with an `Idempotency-Key`, so a retry after a 429 or a timeout never creates a duplicate.
