Rate limits
How many requests each API key can make, the headers that tell you where you stand, and how to back off when you get a 429.
Each API key can make 120 requests per minute. The limit keeps the API fast for everyone and stops a runaway script from burning through your credits. Every response tells you how much of the limit you have left, so a well-behaved client never needs to hit it.
How the limit works
- The limit is per key, counted in one-minute windows.
- Every request counts, whatever its result, including errors.
- Two keys in the same workspace each get their own 120 requests a minute.
- Webhook deliveries from OmniLead to you don't count.
The limit is separate from credits. A search page costs 1 credit and 1 request; a lead update costs 0 credits and 1 request.
Rate limit headers
Every response includes three headers:
HTTP/2 200
content-type: application/json
ratelimit-limit: 120
ratelimit-remaining: 117
ratelimit-reset: 42When you hit the limit
Request number 121 within a window gets a 429 Too Many Requests response with a Retry-After header, in seconds:
{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Rate limit of 120 requests per minute exceeded. Retry after 18 seconds.",
"doc_url": "https://omni.cloudgens.net/docs/api/errors#rate_limited"
}
}Nothing happens on a rate-limited request: no credits are charged and no data changes. Wait the number of seconds in Retry-After and send it again.
Retry with backoff
Retry 429 and 5xx responses with exponential backoff, and always honor Retry-After when it's there. Send the same Idempotency-Key on retried POST requests so nothing is done twice.
async function omnilead(path, init = {}, attempt = 0) {
const res = await fetch(`https://omni.cloudgens.net/api/v1${path}`, {
...init,
headers: {
Authorization: `Bearer ${process.env.OMNILEAD_API_KEY}`,
"OmniLead-Version": "2026-09-28",
...init.headers,
},
});
if ((res.status === 429 || res.status >= 500) && attempt < 5) {
const retryAfter = Number(res.headers.get("retry-after"));
const wait = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 500 + Math.random() * 250;
await new Promise((r) => setTimeout(r, wait));
return omnilead(path, init, attempt + 1);
}
return res;
}import os
import random
import time
import requests
def omnilead(method, path, attempt=0, **kwargs):
resp = requests.request(
method,
f"https://omni.cloudgens.net/api/v1{path}",
headers={
"Authorization": f"Bearer {os.environ['OMNILEAD_API_KEY']}",
"OmniLead-Version": "2026-09-28",
**kwargs.pop("headers", {}),
},
timeout=30,
**kwargs,
)
if (resp.status_code == 429 or resp.status_code >= 500) and attempt < 5:
retry_after = int(resp.headers.get("Retry-After", "0") or 0)
wait = retry_after if retry_after > 0 else (2 ** attempt) * 0.5 + random.random() * 0.25
time.sleep(wait)
return omnilead(method, path, attempt + 1, **kwargs)
return respStay under the limit
- Read the headers. When
RateLimit-Remaininggets low, pause untilRateLimit-Reset. - Use webhooks instead of polling. A webhook on
lead.updatedoremail.repliedreplaces thousands ofGETrequests. - Page with big pages.
limit=100fetches the same data in a quarter of the requests of the default 25. - Batch where the API allows it. One
POST /sequences/{id}/enrollmentscall enrolls up to 500 leads. - Use exports for bulk reads.
POST /exportsreturns every lead in one file, for one request plus a few status checks. - Spread scheduled jobs. Run nightly syncs at a random minute rather than on the hour.
Need more?
If your integration genuinely needs more than 120 requests a minute after using webhooks and exports, contact support with what you're building and your expected volume.
Does the Try it console in the reference count?
Yes. It sends real requests with your test key, so they count toward that key's limit.
Is there a daily limit?
No daily request limit. Search and reveal are limited by your credits.