Handle errors
The error object every API error uses, what each error code means, and how to fix it.
The API uses standard HTTP status codes and returns the same error object for every failure. Each error has a stable code you can branch on, a message written for humans, and a doc_url that links to its explanation on this page.
The error object
{
"error": {
"type": "invalid_request_error",
"code": "invalid_request",
"message": "limit must be between 1 and 100.",
"param": "limit",
"doc_url": "https://omni.cloudgens.net/docs/api/errors#invalid_request"
}
}Every response, including errors, carries a Request-Id header. Include it when you contact support.
Status codes
Error codes
invalid_request
400. A parameter is missing, has the wrong type or fails validation. param names the field, and message says what's expected, for example "limit must be between 1 and 100" or "Send exactly one of email or domain".
Fix: correct the field and send the request again. Don't retry unchanged; it will fail the same way.
authentication_failed
401. The Authorization header is missing, isn't in the form Bearer <key>, or the key has been revoked.
Fix: send Authorization: Bearer ol_live_… with an active key from Settings → API. See Authenticate with API keys.
insufficient_credits
402. The workspace doesn't have enough credits for a search page or a reveal. Nothing was charged.
Fix: buy a credit pack, move to a plan with more credits, or wait for your plan credits to renew. Check the balance first with GET /credits if you're about to run a large batch.
permission_denied
403. The workspace's plan doesn't include what you asked for. Usually the workspace isn't on Agency, which is the plan that includes the API. It also happens when you search a lens the plan doesn't include.
Fix: upgrade the workspace in Settings → Billing, or use a lens your plan includes.
not_found
404. No object with that ID exists in this workspace. IDs are scoped to a workspace, so a key from one workspace can't see another's objects, and deleted objects return 404.
Fix: check the ID, and check you're using the key for the right workspace.
conflict
409. The request conflicts with current state. Common causes:
- Creating a lead with an email that already exists in the workspace. The message includes the existing lead's ID.
- Reusing an
Idempotency-Keywith a different request body. - Adding a tag or suppression that's being changed by another request at the same moment.
Fix: fetch the current object and decide what to do, or use a new Idempotency-Key for a genuinely new request.
rate_limited
429. More than 120 requests in one minute with this key. The response includes Retry-After.
Fix: wait the number of seconds in Retry-After, then retry with backoff. See Rate limits.
internal_error
500. Something failed on our side. It's safe to retry: send the same Idempotency-Key on POST requests so nothing is done twice.
Fix: retry with exponential backoff. If it keeps happening, contact support with the Request-Id header.
Errors that aren't errors
Some outcomes are normal results rather than errors, so they return 2xx:
- A reveal where no verifiable contact was found returns
200withstatus: "no_contact_found"andcredits_charged: 0. - Revealing an entity the workspace already revealed returns
200withalready_revealed: trueandcredits_charged: 0. - Enrolling leads returns
201even if some were skipped. Check theskippedarray for each lead's reason:already_enrolled,suppressed,no_email,email_invalid,country_blockedornot_found.
Handle errors in code
const res = await fetch("https://omni.cloudgens.net/api/v1/reveals", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OMNILEAD_API_KEY}`,
"OmniLead-Version": "2026-09-28",
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({ entity_id: "ent_inv_3Jd8Kq" }),
});
if (!res.ok) {
const { error } = await res.json();
switch (error.code) {
case "insufficient_credits":
// Pause the batch and alert someone to top up.
break;
case "rate_limited":
// Wait for Retry-After, then retry with the same Idempotency-Key.
break;
default:
throw new Error(`${error.code}: ${error.message} (${res.headers.get("request-id")})`);
}
}import os
import uuid
import requests
resp = requests.post(
"https://omni.cloudgens.net/api/v1/reveals",
headers={
"Authorization": f"Bearer {os.environ['OMNILEAD_API_KEY']}",
"OmniLead-Version": "2026-09-28",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"entity_id": "ent_inv_3Jd8Kq"},
timeout=30,
)
if not resp.ok:
error = resp.json()["error"]
if error["code"] == "insufficient_credits":
pass # Pause the batch and alert someone to top up.
elif error["code"] == "rate_limited":
pass # Wait for Retry-After, then retry with the same Idempotency-Key.
else:
raise RuntimeError(f"{error['code']}: {error['message']} ({resp.headers.get('Request-Id')})")