Skip to page content
Docs
Make Agent Fast documentation

Rate limits

Design for the exact per-key plan limits, response headers, and safe retry behavior.

At a glance

Design for the exact per-key plan limits, response headers, and safe retry behavior.

API request pathScoped key → versioned resource → signed response
App / SDKBearer keymaf_live_…/api/v1/…ResourceJSON
Per keyOne minuteRetry-After

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 planRequests per key per minute
Launch120
Operate600
Scale1,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/json

Retry-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 100 records per page instead of repeatedly requesting very small pages.
  • Process webhook events instead of polling conversations, leads, 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#

ResponseRetry?Condition
429YesWait for Retry-After, add jitter, and cap attempts
500SometimesSafe reads or mutations protected by the same idempotency key
409 processing conflictYesWait for its Retry-After: 2 header and keep the same idempotency key
Other 409Not immediatelyRead the current resource state and resolve the conflict
503 outcome_unknownNoThe mutation may have completed; check the resource before using a new idempotency key
400, 401, 403, 404, 415NoCorrect 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.