# Rate limits

Limits are per application and per account, not per IP. Your whole fleet shares one budget for one host, and each host you are connected to has its own.

## Headers

Every response carries your current position, not just a 429.

```
X-RateLimit-Limit-Read: 600
X-RateLimit-Remaining-Read: 573
X-RateLimit-Reset-Read: 1756654920
```

The suffix is `Read` or `Write`, matching what the call you just made spends. A write carries the `Write` trio and never the `Read` one, so look for the suffix rather than a fixed header name.

Two budgets are charged on every call — the per-account one and the client-wide one — but only one trio comes back: whichever of the two is closer to running out. Reporting the roomier number would be worse than reporting nothing, because the one you can see is then not the one that will stop you.

Read `Remaining` and slow down before you hit zero. A client that only reacts to 429s is a client that generates them.

## When you exceed one

```
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Scope: account
X-RateLimit-Reason: per-account write limit exceeded: 120 requests per 1m
```

```json
{
  "success": false,
  "value": null,
  "error": {
    "detail": "rate_limited",
    "message": "Rate limit exceeded.",
    "requestId": "0f7e4c1a-2b8d-…",
    "data": { "retryAfterSeconds": 12, "scope": "account", "window": "1m" }
  }
}
```

`X-RateLimit-Scope` names which budget you exhausted, so you know whether to slow down against one host or across your whole integration.

## Honoring Retry-After

Wait at least the number of seconds given, then add jitter of 25 percent or more.

Jitter is not politeness. A fleet that all received the same `Retry-After` and all retry at exactly that moment reconstructs the burst that caused the limit, and the second wave is worse than the first because it arrives synchronized.

```python
delay = retry_after * (1 + random.uniform(0, 0.25))
```

## Reads and writes are budgeted separately

Writes are more expensive: each one fans out to a channel manager, an email, sometimes an SMS. Read budgets are correspondingly larger.

Your tier is set at approval from your volume estimate and can be raised. If you are hitting limits during normal operation, that is a conversation rather than something to engineer around; write to info@bookwithhaven.com with your client id and the correlation id of a limited request.

## Staying under

**Prefer webhooks to polling.** Once webhooks ship, an event tells you something changed and one read confirms it. Polling every listing every five minutes is the traffic shape limits exist to bound.

**Use `updated_since`.** Re-walking a portfolio to find the one thing that changed is the expensive way to ask a cheap question.

**Cache what does not move.** A listing's timezone, currency and capacity change rarely. The listing list is not a hot resource.

**Batch where an endpoint offers it**, and do not fan out one request per row when a filtered list would do.

**Back off on 5xx as well as 429.** A retry storm against an origin that is already struggling is how a degraded service becomes an outage.

## What is not limited here

The two discovery documents are CDN-cached constants and are not rate limited. The token endpoint has its own limits, described in [The token endpoint](../01-oauth/03-token-endpoint.md); notably it limits by attempts rather than by successful mints, so a refresh bug slows you down rather than locking you out for a day.
