# Rate Limits

> How Mojeeb Public API rate limits work — 60 requests per minute per API key by default, lower limits of their own on endpoints that read Meta live and on replies, response headers, and how to handle 429 responses.

The Mojeeb Public API is rate-limited per API key. The default is **60 requests per minute** per key, with a fixed-window counter that resets every 60 seconds. The WhatsApp template list, whose answer is read live from Meta, and replies in a conversation have lower limits, each counted in a budget of its own.

## What is the default limit?

60 requests per minute per API key, across every endpoint that takes a key except those with a limit of their own. Each key has its own counter — multiple keys in the same organization don't share budget. A request rejected by the rate limiter never reaches the send pipeline, never charges quota, and never creates a conversation. `GET /v1/me` reports the default as `rate_limit.requests_per_minute`.

## Do some endpoints have a lower limit?

Yes. The WhatsApp template list, `GET /v1/agents/{agentId}/connections/{connectionId}/whatsapp-templates`, returns what it reads live from Meta, so it allows **10 requests per minute** per key, in a budget of its own: its calls don't spend your 60, and your other calls don't spend its 10. Replying in a conversation, `POST /v1/agents/{agentId}/conversations/{conversationId}/messages`, allows **20 requests per minute** per key (every call to it counts, including idempotent replays, `409`s and refused replies), also in a budget of its own: each reply goes out to a customer and pauses the AI, so sending gets less room than reading, and your reads never wait for your sends. See [Conversations and Messages](./agents/conversations). Testing a custom API tool also has a limit for your whole company, 30 tests a minute across its keys and the dashboard (`429 test_rate_limited`), on top of your key's budget; see [Custom API Tools](./agents/custom-api-tools).

Each one's reference page shows its limit as `x-rate-limit-per-minute`, and its `429` reports it in `X-RateLimit-Limit`. The template list's limit protects the WhatsApp number's Meta quota, which your sends rely on: keep the list instead of reading it before every send, and read it again after your templates change. Each template send already reads the number's templates from Meta to check the name and language, so listing before every send doubles those reads. See [List WhatsApp Templates](./whatsapp/list-templates).

## Can I get a higher limit?

Not from the dashboard. Every key gets 60 requests per minute today, 10 on the template list and 20 on replies. If a high-volume integration needs more, contact support with your expected peak rate and use case.

## What happens when I hit the limit?

The API responds with `429 Too Many Requests`, the standard error envelope, and full rate-limit headers:

```http
HTTP/1.1 429 Too Many Requests
Retry-After:           60
X-RateLimit-Limit:     60
X-RateLimit-Remaining: 0
X-RateLimit-Reset:     1730212354
Content-Type:          application/json

{
  "error": {
    "type":           "rate_limit_error",
    "code":           "rate_limit_exceeded",
    "message":        "Too many requests. Try again in 60 seconds.",
    "correlation_id": "req_01HQZ..."
  }
}
```

| Header | Meaning |
|---|---|
| `Retry-After` | Seconds to wait before the next request |
| `X-RateLimit-Limit` | The per-minute limit of the endpoint you called (60, 10 on the template list, or 20 on replies) |
| `X-RateLimit-Remaining` | Requests left in the current window — `0` on rejection |
| `X-RateLimit-Reset` | Unix timestamp at which the window resets |

## How should I handle 429 responses?

**Honor `Retry-After`.** Sleep for at least that many seconds, then retry the same request. Aggressively retrying without backing off will keep you throttled.

A solid retry loop:

1. Send the request
2. If `429`, parse `Retry-After`
3. Sleep for `Retry-After` seconds (plus a small jitter, e.g. random 0–2s, to avoid thundering-herd retries from many clients)
4. Retry — pair the retry with the original `Idempotency-Key` if you used one, so the eventual success doesn't double-send

For sustained traffic, smooth your send rate to stay under the limit instead of bursting and backing off — predictable rate beats reactive throttling.

## Are there per-success-response headers?

Currently, no. `X-RateLimit-*` headers are emitted reliably on `429` rejections only. Per-2xx response counter headers are a documented gap in v1 and will land in a follow-up. Until then, rely on 429 + `Retry-After` for backoff signals.

## Common questions

### Does the limit apply to status lookups?

Yes — the default limit covers every `/v1/` endpoint that takes a key, including `GET /v1/whatsapp/messages/{id}`; only the template list and replies in a conversation count apart. If you poll status frequently for many messages, factor that into your budget or batch your polling. `GET /v1/openapi.yaml` needs no key and is limited per IP address instead.

### What happens if my organization has multiple API keys?

Each key has its own independent budget. A 60/min limit on Key A doesn't reduce Key B's available requests. This makes it easy to give different integrations independent budgets — a marketing automation key can be exhausted without affecting a transactional notification key.

### Is there an organization-wide ceiling?

Not on requests; only custom API tool tests have a company-wide limit (see [Custom API Tools](./agents/custom-api-tools)). A customer with 10 keys at 60/min each can theoretically do 600/min organization-wide. We may add an org-level ceiling in a future release if needed; you'll be notified before any change.

### What if I use the same key from multiple servers?

The limit is per key, not per server. Multiple servers sharing one key share the budget. For independent budgets across servers, issue separate keys.

### Does Meta's WhatsApp rate limit interact with this?

Yes — Mojeeb's per-key limit is separate from Meta's per-business-number rate limit. A send accepted by Mojeeb (under the per-key budget) can still be throttled or rejected by Meta downstream. Meta-side throttling is never a `429` from us; it shows on `GET /v1/whatsapp/messages/{id}` as `status: failed`. For a template, and for any message Meta fails after accepting it, `failure_reason` is `RateLimited` and `failure_code` holds Meta's code (e.g. 130429, 131056). A free-form text that Meta refuses at send time currently reads `failure_reason: Unknown` with no `failure_code`, whatever the cause. When Meta throttles the template list itself, the list answers `503 meta_unavailable`: retry with backoff.
