# `rate_limited`: Rate limited

> HTTP 429. A rate limit budget or a daily send limit is spent. Wait `Retry-After` seconds; the message names which limit it was.

Each credential has several budgets, counted separately, and a request is charged to every budget it belongs to. Every response (not just this one) reports them: `RateLimit-Policy` lists each budget, and `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` describe the one closest to running out. `GET /rate-limit` returns the same numbers as JSON. The budgets are per credential (per API key, or per OAuth authorisation), not per IP address, and the numbers below are the defaults.

The message names the budget that ran out. That matters, because they are not interchangeable: running out of the audience budget does not stop you making other calls, and running out of the general one does.

Sending has a second kind of limit, a **daily** cap that is not one of the `RateLimit-Policy` budgets and does not reset with them. When one of those answers, the message says so and `Retry-After` is measured in hours rather than seconds.

## Why it happens, and how to fix it

### 1. The general budget is spent

Every request made with the credential counts against `general`, by default 600 requests per 60 seconds. Tight polling loops, parallel pagination and retry storms are the usual reasons.

**Fix:** Wait `Retry-After` seconds, then continue. Watch `RateLimit-Remaining` and slow down before it reaches zero. Prefer webhooks to polling for changes.

### 2. The audience budget is spent

Reads that return subscriber or recipient email addresses (`GET /newsletters/{newsletter}/subscribers`, `GET /subscribers/{subscriber}`, `GET /newsletters/{newsletter}/insights` and `GET /newsletters/{newsletter}/events`) also count against `audience`, by default 60 per 60 seconds.

**Fix:** Page with `limit=100` rather than many small pages, cache what you have read, and keep doing other work while you wait: the other budgets are unaffected.

### 3. The write budget is spent

Requests that change something also count against `write`, by default 60 per 60 seconds, because each one can publish an event to someone's endpoint.

**Fix:** Batch where the API allows it (for example `POST /tags/{tag}/subscribers` tags up to 500 subscribers in one request) and pace the rest under the limit.

### 4. The daily limit on sends to a list

`POST /articles/{article}/send` and `POST /articles/{article}/schedule` are limited, by default, to 25 dispatches per newsletter per day, counted across every credential. It counts dispatches, never recipients, so the size of the list does not matter. A schedule counts as a send, and a refused send counts for nothing.

**Fix:** Wait the `Retry-After` the response gives (hours, not seconds). If an integration is hitting this, it is probably sending the same article repeatedly; check your retry logic and use the same `Idempotency-Key` for retries.

### 5. The daily limit on test sends

`POST /articles/{article}/test-send` is limited, by default, to 50 per credential per day. Each test send request counts once, whether it goes to one address or five, and one that was refused counts for nothing. It is the only operation that mails people who never subscribed, so it is counted apart.

**Fix:** Wait `Retry-After` (hours, not seconds). Put every address you want a copy at into one request's `to` (up to 5) rather than sending one request per address.

## Retrying

Yes: send the same request again after `Retry-After` seconds (with the same `Idempotency-Key` for a write), and not before.

## Headers

- `Retry-After`: Whole seconds to wait before retrying. Always at least 1.
- `RateLimit-Policy`: Every budget that applies to the request, as `"name";q=<limit>;w=<window seconds>`, comma separated. Daily send limits are not listed here.
- `RateLimit-Limit`: The size of the budget closest to running out.
- `RateLimit-Remaining`: Requests left in that budget.
- `RateLimit-Reset`: Seconds until that budget refills.

## Example response

```json
{
  "error": {
    "code": "rate_limited",
    "message": "This key has spent its \"audience\" rate limit budget of 60 requests per 60 seconds. Reads that return subscriber or recipient email addresses, counted separately from and more tightly than everything else. Retry in 23 seconds; RateLimit-Policy on this response lists every budget that applies.",
    "request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
    "docs_url": "https://usecommune.dev/errors/rate_limited"
  }
}
```

Often confused with: [`conflict`](https://usecommune.dev/errors/conflict.md), [`service_unavailable`](https://usecommune.dev/errors/service_unavailable.md).
