# Rate limits

Each API key can make a set number of requests a minute, shared between the REST API and the platform MCP server. Past it, wait for Retry-After.

Limits protect your account and the AI receptionist from a runaway integration. They are counted per API key, per minute.

## Limits

| What | Limit |
| --- | --- |
| Requests per API key | 120 a minute, shared by the REST API and the [platform MCP server](https://vocenya.com/docs/mcp) |
| Live chat replies (`POST /chats/{chat}/messages`) | 30 a minute per key, within the overall limit |
| Requests with a missing or invalid key | 30 a minute per IP address |
| Public MCP servers (`/mcp/site`, `/mcp/docs`) | 60 requests a minute per IP address each |

Need more for a real use case? Contact us and tell us what you are building.

## Headers

Responses carry the state of the per-key limit:

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `Retry-After` | On a `429`, seconds to wait before trying again. |

## Handling 429

Over the limit, the API answers `429 Too Many Requests`. Wait for `Retry-After` seconds, then retry. A simple, polite client:

```javascript
async function vocenya(path, options = {}) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const response = await fetch(`https://vocenya.com/api/public/v1${path}`, {
      ...options,
      headers: {
        Authorization: `Bearer ${process.env.VOCENYA_API_KEY}`,
        ...options.headers,
      },
    });

    if (response.status !== 429) {
      return response;
    }

    const seconds = Number(response.headers.get('Retry-After') ?? 1);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
  }

  throw new Error('Still rate limited after 5 attempts');
}
```

```python
import os
import time

import requests


def vocenya(method, path, **kwargs):
    headers = {"Authorization": f"Bearer {os.environ['VOCENYA_API_KEY']}"}

    for _ in range(5):
        response = requests.request(method, "https://vocenya.com/api/public/v1" + path, headers=headers, **kwargs)
        if response.status_code != 429:
            return response
        time.sleep(int(response.headers.get("Retry-After", "1")))

    raise RuntimeError("Still rate limited after 5 attempts")
```

## Staying under the limit

- Use [webhooks](https://vocenya.com/docs/webhooks) instead of polling for new records.
- Ask for 100 records per page when you page through a list.
- Give each integration its own key, so one busy job does not slow the others down.
