# Rate Limits

# Rate Limits

Hostex applies rate limiting at three layers to keep the service stable
for everyone. Exceeding a limit returns `HTTP 200` with `error_code: 429`
in the body and a `Retry-After` header (in seconds).

***

## Layer 1 — Per-token (user) limit

Applies to **all v3 endpoints combined**, keyed on your access token.

| Window    | Max requests |
| --------- | ------------ |
| 1 minute  | 1,200        |
| 5 minutes | 12,000       |
| 1 hour    | 20,000       |
| 24 hours  | 100,000      |

These four are checked in parallel — you hit `429` as soon as **any** one
is breached.

## Layer 2 — Per-token + per-endpoint limit

Applies to one endpoint at a time, keyed on `(access token, request path)`.

| Endpoint pattern                                                       | 1 min | 5 min | 1 h    | 24 h   |
| ---------------------------------------------------------------------- | ----- | ----- | ------ | ------ |
| `POST /v3/availabilities`                                              | 120   | —     | —      | —      |
| `POST /v3/listings/*` (prices / inventories / restrictions / calendar) | 120   | —     | —      | —      |
| `POST /v3/reservations`                                                | 60    | —     | —      | —      |
| **All other endpoints**                                                | 600   | 6,000 | 10,000 | 50,000 |

A dash means that window is not enforced for that pattern (the user-level
limit still applies).

## Layer 3 — Per-thread throttle on `POST /conversations/{id}`

Sending a message to a guest is throttled per thread (independent of your
access token), because OTAs aggressively rate-limit channel-side message
APIs and Hostex must shield the channel account from suspensions:

| Window     | Max messages per thread |
| ---------- | ----------------------- |
| 5 seconds  | 5                       |
| 60 seconds | 10                      |
| 30 minutes | 30                      |
| 2 hours    | 60                      |
| 24 hours   | 120                     |

All five are checked in parallel. Plan templates and HostGPT auto-replies
accordingly.

***

## Response on rate-limit hit

```http
HTTP/1.1 200 OK
Retry-After: 60
X-RateLimit-Scope: user            # or "endpoint"
X-RateLimit-Window: 1m
X-RateLimit-Reason: user rate limit exceeded: 1200 requests per 1m

{"error_code":429,"error_msg":"Too Many Attempts.","request_id":"RT..."}
```

For Layer 3 (message throttle) the body explicitly tells you when to retry:

```json
{"error_code":429,"error_msg":"Too many requests. Try again in 60 seconds.","request_id":"RT..."}
```

***

## How to stay under the limit

1. **Always send a meaningful `User-Agent`** — e.g. `MyCleaningApp/1.2.3
   (contact@example.com)`. It helps Hostex bypass IP-level bot defences
   and lets the on-call team contact you about behaviour issues.
2. **Cache dictionary endpoints aggressively.** `GET /custom_channels`,
   `GET /income_items`, `GET /expense_items`, `GET /income_methods`,
   `GET /expense_methods`, `GET /tags`, `GET /groups` change rarely —
   refresh once an hour at most, not on every reservation create.
3. **Batch where possible.** `POST /listings/prices` accepts a list — one
   call per listing-day-range, not per day.
4. **Add jitter on retry.** When you get `429`, exponential backoff
   starting at the `Retry-After` value, plus ±25% random jitter, prevents
   thundering-herd against the next window.
5. **Don't tail webhooks with polling.** Subscribe to
   [Webhooks](https://apidoc.hostex.io/reference/webhook-useage-guide) instead of polling
   `GET /reservations` every minute — the events arrive in < 5 s.
6. **Use `id` filters when querying after a write.** Re-querying a single
   record via `GET /reservations?id=…` is far lighter than re-pulling the
   whole list to find the one you just changed.

***

## What is **not** a rate-limit

| Error                                                 | Looks like 429 but isn't                                                                                           |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `420 Subscription expired / Basic edition`            | Account-level issue. Do not retry — the host must take action in the portal.                                       |
| `429 Too many attempts.` from `/oauth/authorizations` | OAuth brute-force guard, not the user-level rate limit. Stop attempting for 10 minutes after >10 failed exchanges. |