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
| Limit | Applies to |
|---|---|
api_key | Requests per minute for one key. Set by your plan; a key can have a lower limit of its own. |
public_ip | Public keys only: 5 submissions per 10 minutes from one visitor address. |
tenant | All requests of your workspace together, including people using the app. |
ip | All 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/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-000000000000When 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/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
// 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));
}
}