Getting started

Rate limits

Requests are rate limited per API key. When you exceed the limit, the API returns 429 instead of queueing — your request is never silently delayed.

  • 429Returned, never queued
  • 10–600 RPMPublished plan ceilings
  • GETRetries are always safe

The 429 response

429 · rate_limited

Rate-limited requests use the standard error envelope described in Errors:

Response429json
{  "success": false,  "data": null,  "pagination": null,  "meta": {    "error": {      "statusCode": 429,      "type": "rate_limited",      "detail": "Rate limit exceeded. Retry with backoff."    }  }}

Per-plan ceilings

Requests / minute

The ceiling is a plan property: every plan grants a requests-per-minute figure for its key, and a request over that figure returns 429 rather than being queued. The register below reads off the same record the pricing pagepublishes, where each plan's price and monthly credit grant sit beside it.

Free
10 RPMExplore the API and evaluate core endpoints.
Launch
60 RPMFor prototypes, internal tools, and early-stage products.
Growth
120 RPMFor production apps and recurring enrichment workflows.
Scale
300 RPMFor high-volume SaaS products, CRMs, and data pipelines.
Volume
600 RPMFor large-scale recurring enrichment workloads.
Enterprise
Custom rate limitsCustom volume, SLA, DPA, procurement, and reserved capacity.

If steady traffic keeps returning 429after backoff, the client is not misbehaving — its sustained rate is simply above the plan's ceiling. Slow the worker, spread the run over a longer window, or move to the plan whose ceiling matches the throughput you need.

Handling 429s

Bounded retries
  • Retry with bounded exponential backoff — start at 1 second and double on each attempt. Do not retry aggressively.
  • Add jitter when several workers share one key, so a burst of rate-limited workers spreads out instead of retrying in step and hitting the ceiling together again.
  • All endpoints are GET requests, so retries are safe — there are no side effects to worry about.
  • Cache responses where you can, and slow the client instead of looping on failures.
  • Reuse discovery results: filter and location ids from the search discovery endpoints are stable enough to fetch once and reuse.
Bounded backoffts
async function fetchWithBackoff(url: URL, attempts = 3) {  for (let attempt = 0; attempt < attempts; attempt += 1) {    const res = await fetch(url, {      headers: { "X-API-Key": process.env.ENVO_API_KEY ?? "" },    });    if (res.status !== 429) {      return res.json();    }    // 1s, 2s, 4s — bounded, no aggressive retry loops.    await new Promise((resolve) =>      setTimeout(resolve, 1000 * 2 ** attempt),    );  }  throw new Error("Rate limited after bounded retries.");}

The same pattern in Python, with jitter for a shared key:

Bounded backoff with jitterpython
import osimport randomimport timeimport requestsdef fetch_with_backoff(url: str, attempts: int = 3):    for attempt in range(attempts):        res = requests.get(            url,            headers={"X-API-Key": os.environ["ENVO_API_KEY"]},            timeout=30,        )        if res.status_code != 429:            return res.json()        # 1s, 2s, 4s, plus jitter so parallel workers spread out        # instead of retrying in step.        time.sleep(2 ** attempt + random.random())    raise RuntimeError("Rate limited after bounded retries.")

Try it with your own records

Sign up for an API key — 100 free credits, no credit card — and the same key works on every documented endpoint.

Get API key

Keep reading