# Rate limits and quota

Two separate limits apply, and they fail differently.

The **rate limit** is per minute and protects the service from a burst. The **quota** is per calendar month, UTC, and is what you are paying for. Hitting the first means slow down; hitting the second means the month is spent.

| Plan | Rate | Monthly quota |
| --- | --- | --- |
| Starter | 60 per minute | 10,000 |
| Team | 300 per minute | 100,000 |
| Enterprise | 1,000 per minute | 1,000,000 |

## Headers

Every authenticated response carries the current state, so you never have to guess.

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` | Requests allowed this minute on this plan. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `Retry-After` | Seconds to wait. Present on both kinds of 429. |
| `X-Request-Id` | Unique id for this request. Quote it in support. |

## The two 429s

```http Rate limited
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 60

{"error":{"code":"rate_limited","message":"Rate limit exceeded. Slow down and retry."}}
```

```http Quota exceeded
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 1209600

{"error":{"code":"quota_exceeded","message":"Monthly quota exceeded."}}
```

Read the `code`, not the status. `rate_limited` clears within the minute. `quota_exceeded` clears when the UTC month rolls over or when you change plan, and its `Retry-After` can be a fortnight, so a client that blindly sleeps on it will hang.

## Backoff

Sleep for `Retry-After` seconds and retry once. If you are running many workers, add jitter so they do not all wake at the same instant.

A `503 unavailable` is retryable with exponential backoff. A `400`, `401`, `403` or `404` is not: retrying an unchanged request produces the same answer.

## Spending less

- Send `If-None-Match` with the previous `ETag`. A `304` costs a request but no payload, and the brief and story detail change rarely.
- Poll the [change feed](/developers/docs/endpoints#topics-and-changes) rather than re-listing everything. `GET /api/v1/changes?since=…` returns only what moved.
- Raise `limit` to 100 rather than paging in 40s.
