> For the complete documentation index, see [llms.txt](https://chekin.gitbook.io/chekin-public-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chekin.gitbook.io/chekin-public-api/api-reference/rate-limits.md).

# Rate Limits

How many requests you can send to the Chekin API, and what to do when you hit the limit.

To keep the API fast and available for everyone, Chekin limits how many requests can be sent in a given period of time. When you go over a limit, the API replies with `429 Too Many Requests` instead of processing the request.

Limits apply to both the staging and production environments.

### How limits are counted

Requests are counted in **two independent buckets**, and a request is rejected as soon as **either** bucket is full:

* **Per account** — all requests authenticated with the same Chekin account, no matter where they come from.
* **Per IP address** — all requests coming from the same source IP, no matter which account they are authenticated with.

{% hint style="warning" %}
The per-IP bucket is shared by everything behind the same public IP address. If you run several Chekin accounts from one server, or your traffic leaves through a single NAT gateway or proxy, those accounts consume the **same** per-IP allowance. Plan your throughput against the IP limit, not just the account limit.
{% endhint %}

Each bucket is a rolling window: an allowance of `N` requests per minute means no more than `N` requests are accepted in any 60-second window.

### Current limits

| Endpoints                                                                          | Per account  | Per IP address |
| ---------------------------------------------------------------------------------- | ------------ | -------------- |
| <p>Housings — all operations<br><code>/public/api/v1/housings/…</code></p>         | 200 / minute | 200 / minute   |
| <p>Reservations — all operations<br><code>/public/api/v1/reservations/…</code></p> | 300 / minute | 200 / minute   |
| <p>Create Guest<br><code>POST /public/api/v1/guests/</code></p>                    | —            | 50 / minute    |

{% hint style="info" %}
**Create Guest is the tightest limit.** At 50 requests per minute per IP it is the one most likely to be reached during a bulk import or a backfill. If you are loading guests in bulk, pace the requests — roughly one per second — rather than sending them as fast as your client allows.
{% endhint %}

Endpoints not listed above are not individually limited today, but Chekin may apply limits to any endpoint. Build your integration so that a `429` on **any** request is handled gracefully.

### The 429 response

When a limit is reached, the API responds with HTTP status `429` and a `Retry-After` header giving the number of seconds to wait before the request will be accepted:

{% tabs %}
{% tab title="Response headers" %}

```
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 27
```

{% endtab %}

{% tab title="Response body" %}

```json
{
  "detail": "Request was throttled. Expected available in 27 seconds.",
  "errors": []
}
```

{% endtab %}
{% endtabs %}

A `429` means the request was **not** processed. Nothing was created, updated or deleted, so the request is safe to send again once you have waited.

### Handling limits in your integration

* **Respect `Retry-After`.** It tells you exactly how long to wait. Retrying before that time returns another `429`.
* **Back off exponentially** if you retry without reading the header: wait 1s, then 2s, then 4s, up to a sensible maximum, and give up after a few attempts rather than retrying forever.
* **Do not retry in a tight loop.** Immediate retries keep the bucket full and extend the time you spend limited.
* **Spread bulk work out.** For imports and backfills, send requests at a steady pace instead of in bursts, and process items sequentially rather than firing every request in parallel.
* **Cache what does not change.** Housing and room data rarely changes between calls — re-reading it before every write is the most common way integrations reach a limit unnecessarily.
* **Log `429` responses.** A rising number of them is an early warning that your integration is about to start losing work.

### Example: retrying with `Retry-After`

{% tabs %}
{% tab title="JavaScript" %}

```javascript
async function requestWithRetry(url, options, maxAttempts = 4) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const response = await fetch(url, options);

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

    if (attempt === maxAttempts) {
      throw new Error('Rate limit reached, giving up after ' + maxAttempts + ' attempts');
    }

    const retryAfter = Number(response.headers.get('Retry-After')) || 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
  }
}
```

{% endtab %}

{% tab title="Python" %}

```python
import time
import requests


def request_with_retry(method, url, max_attempts=4, **kwargs):
    for attempt in range(1, max_attempts + 1):
        response = requests.request(method, url, **kwargs)

        if response.status_code != 429:
            return response

        if attempt == max_attempts:
            raise RuntimeError(f"Rate limit reached, giving up after {max_attempts} attempts")

        retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
        time.sleep(retry_after)
```

{% endtab %}
{% endtabs %}

### Need a higher limit?

If your integration needs more throughput than the limits above allow, contact the Chekin team at <gianluca@chekin.com> with an estimate of the request volume you expect and the endpoints involved.
