# Rate limits (/en/api/rate-limit)

Tebar limits requests on two levels:

1. **A general per-minute limit** on every request made with an API key or OAuth token.
2. **Per-feature limits** on actions that are costly or easy to abuse, such as syncs and uploads.

Social platforms have their own limits too, such as posts per day. Tebar doesn't set those (see [Platform limits](#platform-limits)).

## General limit [#general-limit]

The general limit counts every request from the API keys and OAuth tokens of one Tebar account, REST API and MCP together. The dashboard isn't counted.

**Limit = 60 requests a minute + 5 for each active social account, at most 600.**

| Active social accounts | Requests a minute |
| ---------------------- | ----------------- |
| 0                      | 60                |
| 1                      | 65                |
| 10                     | 110               |
| 50                     | 310               |
| 108 or more            | 600               |

It's counted in 1-minute windows. All API keys of the same account share one count, so a new key doesn't raise the limit. Tebar doesn't limit how many posts you make; only your plan's quota and the platforms' rules do.

### Headers on every response [#headers-on-every-response]

| Header                | Meaning                             |
| --------------------- | ----------------------------------- |
| `RateLimit-Limit`     | This account's limit per minute     |
| `RateLimit-Remaining` | Requests left this minute           |
| `RateLimit-Reset`     | Seconds until the count starts over |

Use `RateLimit-Remaining` to slow down before you run out, for example when working through a large queue.

## When you go over the limit [#when-you-go-over-the-limit]

The API answers `429 Too Many Requests` with a `Retry-After` header (seconds) and this body:

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests: 65 a minute for this account. Try again in 23 seconds.",
    "details": { "retry_after_seconds": 23, "limit": 65 }
  }
}
```

Wait for `Retry-After`, then send the same request again. A request refused with 429 isn't processed at all, so it's safe to resend. For `POST /v1/posts`, keep the same `Idempotency-Key` so a post doesn't go out twice if an earlier answer got lost on the way.

```js
async function tebar(path, init = {}, tries = 3) {
  const res = await fetch(`https://api.tebar.app/v1${path}`, {
    ...init,
    headers: { Authorization: `Bearer ${process.env.TEBAR_API_KEY}`, ...init.headers },
  });
  if (res.status === 429 && tries > 1) {
    const wait = Number(res.headers.get('Retry-After') ?? 5);
    await new Promise((r) => setTimeout(r, wait * 1000));
    return tebar(path, init, tries - 1);
  }
  return res;
}
```

In n8n, turn on **Retry On Fail** on the HTTP Request node and set **Wait Between Tries** to at least 5 seconds.

## MCP [#mcp]

MCP tools share the same general limit as the REST API. Over the limit, the `/mcp` endpoint answers HTTP `429` with a `Retry-After` header and the body `{ "error": "rate_limited", "error_description": "…" }`. Per-feature limits inside a tool show up as that tool's error message, and the AI agent can try again after waiting. One exception: `list_comments` with `refresh: true` doesn't fail when its sync limit is used up; it returns the comments already stored without reading the platform again.

## Per-feature limits [#per-feature-limits]

These apply on top of the general limit and count per Tebar account unless stated otherwise. Over one, the answer is also `429 rate_limited`; `details.retry_after_seconds` is the longest you may need to wait (the window's length).

| Endpoint                                                                               | Limit                           |
| -------------------------------------------------------------------------------------- | ------------------------------- |
| `POST /comments/sync` (and `refresh` on the MCP tool `list_comments`)                  | 1 a minute                      |
| `POST /inbox/sync`                                                                     | 1 a minute                      |
| `POST /insights/sync`                                                                  | 1 every 5 minutes               |
| `POST /inbox/conversations/{id}/messages` (sending a DM)                               | 60 a minute                     |
| Media uploads (every way of uploading, including `media_urls` on a post)               | 300 files an hour               |
| `GET /accounts/{id}/posts/{postId}` and `.../media` (posts made in the platform's app) | 120 an hour                     |
| `GET /accounts/{id}/threads/search`                                                    | 2,000 a day per Threads account |
| `POST /accounts/{id}/threads/search/{postId}/reply`                                    | 60 an hour per Threads account  |
| `POST /links`                                                                          | 60 an hour                      |
| `POST /profiles`                                                                       | 120 an hour                     |
| `POST /profiles/{id}/connect-links`                                                    | 600 an hour                     |
| `POST /webhooks/{id}/test` and `.../deliveries/{deliveryId}/resend`                    | 60 an hour                      |
| `POST /accounts/discord/webhook`                                                       | 30 an hour                      |

There's no need to call syncs over and over: comments, DMs, and insights are kept up to date in the background. To hear about new comments as they arrive, use [outgoing webhooks](/api/webhook) instead of polling.

## Platform limits [#platform-limits]

Every platform limits posts per account per day and calls to its own API. These limits belong to the social account, Tebar can't raise them, and posts made in the platform's app or other tools count toward them too.

- Check the posting quota left with `GET /accounts/{id}/publishing-limit` before sending many posts.
- When a platform refuses because of its limit, the post target fails with an `error_code` such as `publish_limit_reached` or `rate_limited` (see [Errors](/api/error)). When the reset time is known, the target stays `pending` and `retry_after` holds the next attempt.
