# Rate limit (/api/rate-limit)

Tebar membatasi request di dua lapis:

1. **Batas umum per menit** untuk semua request lewat API key atau token OAuth.
2. **Batas per fitur** untuk aksi yang mahal atau rawan disalahgunakan, misalnya sinkronisasi dan upload.

Platform sosial juga punya batasnya sendiri, misalnya jumlah post per hari. Batas itu tidak diatur Tebar (lihat [Batas dari platform](#batas-dari-platform)).

## Batas umum [#batas-umum]

Batas umum menghitung semua request dari API key dan token OAuth milik satu akun Tebar, REST API dan MCP digabung. Dashboard tidak ikut dihitung.

**Batas = 60 request per menit + 5 untuk setiap akun sosial aktif, maksimal 600.**

| Akun sosial aktif | Request per menit |
| ----------------- | ----------------- |
| 0                 | 60                |
| 1                 | 65                |
| 10                | 110               |
| 50                | 310               |
| 108 atau lebih    | 600               |

Hitungannya per jendela 1 menit. Semua API key milik akun yang sama berbagi satu hitungan, jadi membuat key baru tidak menambah batas. Jumlah post tidak dibatasi oleh Tebar; batasnya hanya kuota paket dan aturan platform.

### Header di setiap respons [#header-di-setiap-respons]

| Header                | Isi                                       |
| --------------------- | ----------------------------------------- |
| `RateLimit-Limit`     | Batas per menit untuk akun ini            |
| `RateLimit-Remaining` | Sisa request di menit ini                 |
| `RateLimit-Reset`     | Detik sampai hitungan mulai dari nol lagi |

Pakai `RateLimit-Remaining` untuk memperlambat sebelum batas habis, misalnya saat memproses antrean besar.

## Saat batas terlampaui [#saat-batas-terlampaui]

API membalas `429 Too Many Requests` dengan header `Retry-After` (detik) dan 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 }
  }
}
```

Tunggu selama `Retry-After`, lalu kirim ulang request yang sama. Request yang ditolak dengan 429 tidak diproses sama sekali, jadi aman dikirim ulang. Untuk `POST /v1/posts`, tetap sertakan `Idempotency-Key` yang sama supaya post tidak terbit dua kali bila jawaban sebelumnya hilang di jalan.

```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;
}
```

Di n8n, nyalakan **Retry On Fail** di node HTTP Request dan beri jeda (**Wait Between Tries**) minimal 5 detik.

## MCP [#mcp]

Tool MCP berbagi batas umum yang sama dengan REST API. Saat batas terlampaui, endpoint `/mcp` membalas HTTP `429` dengan header `Retry-After` dan body `{ "error": "rate_limited", "error_description": "…" }`. Batas per fitur di dalam tool muncul sebagai pesan error dari tool itu, dan agent AI bisa mencoba lagi setelah menunggu. Pengecualian: `list_comments` dengan `refresh: true` tidak error saat batas sinkronisasinya habis; komentar yang sudah tersimpan tetap dikembalikan tanpa membaca ulang dari platform.

## Batas per fitur [#batas-per-fitur]

Batas ini berlaku selain batas umum dan dihitung per akun Tebar, kecuali disebut lain. Saat terlampaui, jawabannya juga `429 rate_limited`; `details.retry_after_seconds` berisi waktu tunggu paling lama (panjang jendelanya).

| Endpoint                                                                           | Batas                           |
| ---------------------------------------------------------------------------------- | ------------------------------- |
| `POST /comments/sync` (dan `refresh` di tool MCP `list_comments`)                  | 1 per menit                     |
| `POST /inbox/sync`                                                                 | 1 per menit                     |
| `POST /insights/sync`                                                              | 1 per 5 menit                   |
| `POST /inbox/conversations/{id}/messages` (kirim DM)                               | 60 per menit                    |
| Upload media (semua cara upload, termasuk `media_urls` di post)                    | 300 file per jam                |
| `GET /accounts/{id}/posts/{postId}` dan `.../media` (post yang dibuat di aplikasi) | 120 per jam                     |
| `GET /accounts/{id}/threads/search`                                                | 2.000 per hari per akun Threads |
| `POST /accounts/{id}/threads/search/{postId}/reply`                                | 60 per jam per akun Threads     |
| `POST /links`                                                                      | 60 per jam                      |
| `POST /profiles`                                                                   | 120 per jam                     |
| `POST /profiles/{id}/connect-links`                                                | 600 per jam                     |
| `POST /webhooks/{id}/test` dan `.../deliveries/{deliveryId}/resend`                | 60 per jam                      |
| `POST /accounts/discord/webhook`                                                   | 30 per jam                      |

Sinkronisasi tidak perlu dipanggil terus-menerus: komentar, DM, dan insight sudah diperbarui otomatis di belakang. Untuk tahu kapan ada komentar baru, pakai [webhook keluar](/api/webhook) daripada polling.

## Batas dari platform [#batas-dari-platform]

Setiap platform membatasi jumlah post per akun per hari dan jumlah panggilan API-nya sendiri. Batas ini berlaku untuk akun sosialnya, tidak bisa dinaikkan Tebar, dan ikut terpakai oleh post yang dibuat di aplikasi platform atau alat lain.

- Cek sisa kuota posting dengan `GET /accounts/{id}/publishing-limit` sebelum mengirim banyak post.
- Bila platform menolak karena batasnya, target post gagal dengan `error_code` seperti `publish_limit_reached` atau `rate_limited` (lihat [Error](/api/error)). Kalau waktu reset-nya diketahui, target tetap `pending` dan `retry_after` berisi waktu percobaan berikutnya.
