Rate limits
Design for the exact per-key plan limits, response headers, and safe retry behavior.
Design for the exact per-key plan limits, response headers, and safe retry behavior.
The public API applies a rolling one-minute request bucket to each API key or OAuth access token. Creating more keys to evade a limit is unsupported and can lead to credential or account restrictions.
Plan limits#
| Active plan | Requests per key per minute |
|---|---|
| Starter | 120 |
| Pro | 600 |
| Studio | 1,800 |
A credential whose account has no active paid plan receives 403 subscription_required before rate-limit capacity is granted. Make Agent Fast can introduce lower endpoint-specific limits for unusually expensive operations; when that happens, the endpoint reference will identify them explicitly.
A rate-limited response#
When the bucket is exhausted, the API returns 429 rate_limit_exceeded with:
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-Request-Id: 6c0b2f2e-...
Content-Type: application/jsonRetry-After is the minimum number of seconds to wait. Every authenticated
response also includes X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset (a Unix timestamp), so applications can throttle before a
429.
Retry implementation#
Use exponential backoff with random jitter, honor a larger Retry-After, and cap the total number of attempts.
async function requestWithRetry(url: string, init: RequestInit, attempts = 4) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
const response = await fetch(url, init);
if (response.status !== 429 && response.status < 500) return response;
if (attempt === attempts - 1) return response;
const retryAfter = Number(response.headers.get("retry-after") ?? 0) * 1_000;
const exponential = 500 * 2 ** attempt;
const jitter = Math.random() * 250;
await new Promise((resolve) =>
setTimeout(resolve, Math.max(retryAfter, exponential + jitter)),
);
}
throw new Error("unreachable");
}For POST, PATCH, and DELETE, keep the same valid Idempotency-Key across every network or server retry. A new idempotency key can repeat a completed side effect.
Reduce request volume#
- Cache account, site, and configuration reads that do not need real-time freshness.
- Request up to
100records per page instead of repeatedly requesting very small pages. - Process webhook events instead of polling conversations, leads, domain state, or broadcasts.
- Bound worker concurrency per API key; a burst from many serverless invocations shares the same bucket.
- Use separate keys for separate services for isolation and auditability, not to multiply one workload's capacity.
Retry and do-not-retry table#
| Response | Retry? | Condition |
|---|---|---|
429 | Yes | Wait for Retry-After, add jitter, and cap attempts |
500 | Sometimes | Safe reads or mutations protected by the same idempotency key |
409 processing conflict | Yes | Wait for its Retry-After: 2 header and keep the same idempotency key |
Other 409 | Not immediately | Read the current resource state and resolve the conflict |
400, 401, 403, 404, 415 | No | Correct input, credentials, entitlement, path, or content type first |
If a sustained workload legitimately exceeds its plan bucket, reduce polling, batch work, or contact support with representative request IDs and expected traffic rather than adding an unbounded retry loop.