Rate limits
Pace requests so customer updates, campaigns, and journeys keep moving predictably. The API applies three request budgets per API key. Exceeding a budget returns 429 with a Retry-After header.
Services using the same key share its budgets. Coordinate background sync jobs and interactive traffic accordingly.
Buckets
| Bucket | Limit | Window | Applies to |
|---|---|---|---|
read | 100 requests | 1 second | Every authenticated GET (except the form endpoints below). |
write | 25 requests | 1 second | Every other POST, PATCH, DELETE. |
ai | 10 requests | 60 seconds | LLM-backed endpoints. |
Each bucket is an independent counter. Listing records, fetching a record, and GET /v1/me draw from read. Creating, upserting, patching, deleting records, firing events, sending a template, and triggering a workflow draw from write.
AI bucket
These endpoints generate an expression with an LLM and draw from the ai bucket:
| Endpoint | Condition |
|---|---|
POST /v1/segments | Always. |
POST /v1/webhooks, PATCH /v1/webhooks/{id} | Only when the body sets a custom filter condition. |
When the condition is not met, the request draws from write instead.
The AI bucket does not describe every paid operation. Record writes can require AI auto-fill credits, and event preparation can wait for AI capacity or credits after acceptance. Those requests still use the write bucket. Content, evaluated attributes, and broadcast audiences are authored in the app. An ai request that has no credits left returns 402 insufficient_credits, which is separate from the rate limit.
Exceeding a limit
Over the limit, the API returns 429 with the standard error envelope. The code is rate_limit_exceeded:
{
"error": {
"type": "invalid_request_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, please try again later."
}
}
See Errors for the full envelope and the code list.
Retry-After
Rate-limit responses use an HTTP date in UTC to indicate when the window resets. Other 429 responses, such as idempotency_admission_busy, can use delay seconds. Support both formats; see the parser example. Successful responses do not include a remaining-request counter.
Retry-After: Wed, 21 Oct 2026 07:28:00 GMT
Wait until that time, then retry. Add jitter to spread retries.
Event admission
Business events also pass shared admission limits per fleet, space, and schema. These apply to every new occurrence, including each item in a batch; changing API keys does not bypass them. They are operational limits, separate from your subscription and the HTTP request buckets.
| Scope | Sustained rate | Burst | Pending preparation | Retained receipts |
|---|---|---|---|---|
| Fleet | 100 events/second | 200 | 10,000 | 1,000,000 |
| Space | 10 events/second | 30 | 1,000 | 50,000 |
| Schema | 5 events/second | 15 | 500 | 20,000 |
event_rate_limited reports a computed delay. event_backlog_full asks for a 30-second delay. event_receipt_limit reports a one-hour delay, but capacity must actually become available before new occurrences can enter; do not repeatedly replace IDs or keys. Single-event failures use Retry-After; per-item batch failures include retryAfterMilliseconds. A duplicate with matching input recovers its existing receipt without admitting a new occurrence.
Event identity changes have separate space limits of 100,000 aliases and 1,000,000 changes. Capacity failures return 429 without a guaranteed retry header. Preserve alias revisions and reconcile capacity before generating more changes.
Implementation
The rate-limit window is fixed. Avoid using the window boundary as a target for a burst: pace requests throughout it, keep concurrent workers within a shared budget, and add jitter when several workers retry.
Form endpoints
The unauthenticated form endpoints (POST /v1/forms/{id}/submit and the double opt-in link GET /v1/forms/{id}/confirm/{token}) do not use the read/write/ai buckets. They have dedicated limiters:
| Limiter | Limit | Window | Keyed on |
|---|---|---|---|
| Per IP | 10 submissions | 60 seconds | Client IP. |
| Per form | 120 submissions | 60 seconds | Form ID. |
Double opt-in confirmation emails are throttled separately: 2 per recipient per form per hour, and 200 per form per hour. When either trips, the submit endpoint returns 429 with code rate_limit_exceeded. Keep these limits in mind when building a custom form experience.
POST /v1/emails/{emailId}/unsubscribe, the RFC 8058 one-click unsubscribe
target, has no rate limiter.
Handling limits
- Classify
429: wait for rate/backlog limits; resolve exhausted receipt or identity capacity before resuming new writes. - Spread bursty writes over time. The
writebucket is tighter thanread. - Batch where the API supports it. Fire many events in one call with
POST /v1/events/{schema}/batchto reduce HTTP overhead. Each event still consumes event-admission capacity, and each result must be checked. - Records have no batch write. Loop
records.upsertone record at a time and pace the loop under thewritebucket. Keep a checkpoint so a partial sync can resume from its last confirmed result.