> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bluereacher.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits and sending capacity

> How BlueReacher's API request limits differ from per-line daily sending capacity, and how to scale volume by adding lines.

BlueReacher has two limits that people mix up. They are not the same thing and they fail in different ways.

**API rate limits** cap how fast your code can call the API. They are a technical limit. You hit them with a `429` and a `Retry-After` header, you back off, you continue.

**Sending capacity** caps how many new conversations one line starts per day. It is a deliverability limit, not a technical one. It exists to protect how recipients and their devices treat your line. You cannot back off and retry your way past it in the same hour.

|                     | API rate limits             | Sending capacity                       |
| ------------------- | --------------------------- | -------------------------------------- |
| What it counts      | HTTP requests               | New conversations started              |
| Scope               | Per API key                 | Per line                               |
| Window              | Per second and per minute   | Per rolling hour and per day           |
| Fix when you hit it | Retry after the header says | Add lines, or wait for the daily reset |
| Signal              | `429 rate_limit_exceeded`   | `429 line_capacity_reached`            |

## API rate limits

Limits apply per API key, across all lines on that key.

| Endpoint group                              | Limit                   |
| ------------------------------------------- | ----------------------- |
| `POST /messages`                            | 60 requests per minute  |
| Other write endpoints (POST, PATCH, DELETE) | 120 requests per minute |
| Read endpoints (GET)                        | 300 requests per minute |
| Any endpoint, burst ceiling                 | 10 requests per second  |

Every response carries your current counters.

| Header                  | Example      | Meaning                                       |
| ----------------------- | ------------ | --------------------------------------------- |
| `X-RateLimit-Limit`     | `60`         | Requests allowed in the current window        |
| `X-RateLimit-Remaining` | `41`         | Requests left in the current window           |
| `X-RateLimit-Reset`     | `1785945660` | Unix timestamp when the window resets         |
| `Retry-After`           | `12`         | Seconds to wait. Sent on `429` responses only |

Read `X-RateLimit-Remaining` on every response and slow down before you hit zero. That is cheaper than handling `429`s.

### Sending a message

`POST https://api.bluereacher.com/functions/v1/messages`

Authenticate with `Authorization: Bearer bluereacher_your_api_key`.

| Field         | Type   | Required | Description                                                                             |
| ------------- | ------ | -------- | --------------------------------------------------------------------------------------- |
| `from`        | string | yes      | The line sending the message, in E.164 format                                           |
| `to`          | string | yes      | Recipient number in E.164 format                                                        |
| `content`     | string | yes      | Message body                                                                            |
| `channel`     | string | no       | `imessage`, `rcs`, or `sms`. Defaults to `imessage`                                     |
| `external_id` | string | no       | Your own id. Reused values are treated as the same message, so retries never send twice |
| `media_url`   | string | no       | Public URL of an image or file to attach                                                |

```bash theme={null}
curl -i -X POST https://api.bluereacher.com/functions/v1/messages \
  -H "Authorization: Bearer bluereacher_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155550142",
    "to": "+16465550119",
    "content": "Hey Dana, saw you took over ops at Northline. Worth a quick chat?",
    "channel": "imessage",
    "external_id": "seq-4471-step-1"
  }'
```

A `429` looks like this.

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests on this API key. Retry after 12 seconds.",
    "retry_after": 12
  }
}
```

Always set `external_id` before you add retries. Without it, a retry after a timeout can deliver the same message twice.

```python theme={null}
import time
import requests

BASE = "https://api.bluereacher.com/functions/v1/"
HEADERS = {
    "Authorization": "Bearer bluereacher_your_api_key",
    "Content-Type": "application/json",
}

def send(payload, max_attempts=5):
    for attempt in range(max_attempts):
        r = requests.post(BASE + "messages", json=payload, headers=HEADERS)
        if r.status_code != 429:
            r.raise_for_status()
            return r.json()
        code = r.json().get("error", {}).get("code")
        if code == "line_capacity_reached":
            raise RuntimeError("line is out of capacity today, route to another line")
        time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
    raise RuntimeError("still rate limited after retries")
```

Retrying a `line_capacity_reached` error is pointless. That `Retry-After` can be hours. Route the message to another line or queue it for tomorrow.

## Sending capacity

Every BlueReacher line is a dedicated iMessage line, managed for you. Each one has a daily allowance of new conversations.

| Line age         | New conversations per day |
| ---------------- | ------------------------- |
| Days 1 to 7      | 25                        |
| Days 8 to 14     | 50                        |
| Days 15 to 21    | 75                        |
| Day 22 and after | 100                       |

A second cap applies on top: 20 new conversations per line per rolling 60 minutes. This stops a burst from looking like a blast.

A conversation counts as new when you message someone the line has not exchanged messages with in the last 30 days. Replies inside an active thread do not count. A line at its daily cap can still answer everyone who wrote back.

### Why capacity exists

Sender reputation. There is no A2P registration required on a BlueReacher line, so nothing here comes from a carrier registry or a campaign approval queue. The limit comes from recipient behavior.

Recipients report messages as junk. They block numbers. Those signals get counted against the line that sent the message, and a line carrying too many of them starts landing badly for everyone you message from it, including people who wanted to hear from you. Damage compounds and it is slow to undo.

Pacing keeps the signals low. BlueReacher spreads a line's daily allowance across its sending window with randomized gaps rather than firing everything at once. Lines are also monitored for health. If a line's block and report rate climbs past 2 percent of recipients, its daily capacity drops automatically until the rate recovers. If 120 consecutive outbound messages from a line go unanswered, new-conversation sending on that line pauses so you can fix the list or the copy.

### Checking a line

`GET https://api.bluereacher.com/functions/v1/lines/{line_id}`

Authenticate with `Authorization: Bearer bluereacher_your_api_key`.

| Field     | Type   | Required | Description                         |
| --------- | ------ | -------- | ----------------------------------- |
| `line_id` | string | yes      | Path parameter. The line to inspect |

```bash theme={null}
curl https://api.bluereacher.com/functions/v1/lines/lin_8f2a3c91 \
  -H "Authorization: Bearer bluereacher_your_api_key" \
  -H "Content-Type: application/json"
```

```json theme={null}
{
  "line_id": "lin_8f2a3c91",
  "from": "+14155550142",
  "channel": "imessage",
  "status": "active",
  "warmup_stage": "week_3",
  "daily_capacity": 75,
  "sent_today": 62,
  "remaining_today": 13,
  "capacity_resets_at": "2026-07-25T00:00:00Z",
  "created_at": "2026-07-03T16:11:04Z"
}
```

| Field                | Type    | Description                                               |
| -------------------- | ------- | --------------------------------------------------------- |
| `line_id`            | string  | Unique id for the line                                    |
| `from`               | string  | The line's number in E.164 format                         |
| `channel`            | string  | `imessage`, `rcs`, or `sms`                               |
| `status`             | string  | `active`, `warming`, `throttled`, or `paused`             |
| `warmup_stage`       | string  | `week_1` through `week_3`, or `full` once warmup finishes |
| `daily_capacity`     | integer | New conversations allowed today                           |
| `sent_today`         | integer | New conversations started today                           |
| `remaining_today`    | integer | `daily_capacity` minus `sent_today`                       |
| `capacity_resets_at` | string  | ISO 8601 timestamp of the next daily reset, in UTC        |
| `created_at`         | string  | ISO 8601 timestamp of when the line was provisioned       |

Poll this before each batch and route around any line where `remaining_today` is zero or `status` is `throttled`.

## Scale by adding lines, not by pushing one line

Capacity is per line, so volume is a multiplication problem. One line at 100 per day is the ceiling. Ten lines at 100 per day each is 1,000 per day, with every line still inside a safe range.

| Lines | New conversations per day | Per month (22 sending days) |
| ----- | ------------------------- | --------------------------- |
| 1     | 100                       | 2,200                       |
| 5     | 500                       | 11,000                      |
| 20    | 2,000                     | 44,000                      |

Rules that keep this working:

* Sort your list by line, not by line by list. One prospect gets messaged from one line and stays with that line for the whole sequence. Switching mid-thread confuses the recipient and splits your reply history.
* Fill lines evenly. Running three lines at capacity and seven at 10 percent gives you the risk profile of three hot lines.
* Add lines before you need them, not the week you need them. A new line takes three weeks to reach full capacity, so order for the volume you want a month out.
* Watch reply rate per line. A line that sends 100 and gets 2 replies has a copy problem that more lines will only spread around.

## Error reference

| Status | Code                      | Meaning                                 | What to do                                                  |
| ------ | ------------------------- | --------------------------------------- | ----------------------------------------------------------- |
| 429    | `rate_limit_exceeded`     | API request limit for the key           | Wait `Retry-After` seconds, then retry                      |
| 429    | `line_capacity_reached`   | Line used its daily allowance           | Send from another line, or wait for `capacity_resets_at`    |
| 429    | `hourly_capacity_reached` | Line hit 20 new conversations this hour | Retry after `Retry-After`, or use another line              |
| 409    | `line_paused`             | Line paused on health grounds           | Check the line, fix list quality, contact support to resume |
| 400    | `invalid_line`            | `from` is not a line on your account    | Confirm `line_id` with `GET /lines`                         |
