Rate limits

Limits keep the service fast for everyone. They are applied per key, per workspace, and for public keys per visitor.

What is limited

What is limited
LimitApplies to
api_keyRequests per minute for one key. Set by your plan; a key can have a lower limit of its own.
public_ipPublic keys only: 5 submissions per 10 minutes from one visitor address.
tenantAll requests of your workspace together, including people using the app.
ipAll requests from one address.

Headers on every answer

RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the bucket is full again) describe the tightest limit. X-Quota-Leads-Remaining is what is left of this month's lead quota, or unlimited.

HTTP
HTTP/1.1 201 Created
RateLimit-Limit: 60
RateLimit-Remaining: 57
RateLimit-Reset: 3
X-Quota-Leads-Remaining: 4812
X-Request-Id: 01a0f0c2-0000-7000-8000-000000000000

When you hit a limit

You get 429 with error rate_limited, the scope that was hit and retry_after in seconds (also in the Retry-After header). Wait that long, then retry with the same Idempotency-Key.

HTTP
HTTP/1.1 429 Too Many Requests
Retry-After: 12

{
  "error": "rate_limited",
  "message": "Too many requests",
  "scope": "api_key",
  "retry_after": 12,
  "request_id": "01a0f0c2-0000-7000-8000-000000000000"
}

Monthly lead quota

Your plan includes a number of new leads through the API each month. Duplicates, spam and test keys do not count. When it is used up you get 429 with error quota_exceeded and held: true. Do not retry: the submission is kept for 7 days and released automatically when the quota resets or your plan grows.

Retrying in code

Node.js
// Retry a request after the wait the API asks for. Reuse the same Idempotency-Key
// so a retried create never makes a second lead.
async function send(request, attempts = 3) {
  for (let i = 1; ; i++) {
    const res = await fetch(request.url, request.init);
    if (res.status !== 429 || i === attempts) return res;
    const body = await res.clone().json().catch(() => ({}));
    if (body.error === "quota_exceeded") return res; // held until the quota resets, do not retry
    const wait = Number(res.headers.get("Retry-After") ?? body.retry_after ?? 1);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
}