size before exploring a new search#Run with "size": 1 first to see the total result count, then decide how many results you actually need before spending credits.
fields to request only what you need#{
"size": 500,
"fields": ["domain", "name", "companySize", "primaryCountry", "industries"],
"companiesFilters": { ... }
}Reduces response size and latency on high-volume pipelines.
revealEmails#| Approach | When to use | Cost (v3) |
|---|---|---|
| Search People → Reveal Emails | Building a list from scratch | 0.2/result + 1/email found |
Enrich Person with revealEmails: true |
Enriching a known CRM record | 0.1/result + 1/email found |
Both cost the same per-email — the difference is workflow, not price.
Lookalike searches cost the same per result as filter searches on v3. That said, poor seed domains return poor results — test on a small size first and validate seed domains with Warmup before scaling up.
Lookup Companies is cheaper per result than Search Companies with includeDomains. Use Lookup whenever you have a specific list of domains.
| Status | Use in outreach? |
|---|---|
verified |
Yes — SMTP-confirmed via ZeroBounce |
guessed |
Yes — high-confidence pattern match |
catchAll |
Yes — ZeroBounce-verified, catch-all domain accepts all inbound |
notFound |
No — not charged |
| Field | Source | Use for |
|---|---|---|
companySizes |
Ocean.io estimate (bracket) | Quick segmentation |
employeeCountOcean |
Ocean.io estimate (numeric) | Precise thresholds (e.g. > 75) |
employeeCountLinkedin |
LinkedIn self-reported | When LinkedIn count is specifically the signal |
LinkedIn counts lag and can be inflated by contractors.
triggered in enrich responses#triggered means the domain wasn't in the database — crawling started. Wait 2–5 minutes, then retry:
import time
def enrich_with_retry(domains, api_token, max_attempts=3):
pending = domains[:]
results = {}
for attempt in range(max_attempts):
if not pending:
break
response = enrich_companies(pending, api_token)
still_pending = []
for domain, result in response.items():
if result["status"] == "triggered":
still_pending.append(domain)
else:
results[domain] = result
pending = still_pending
if pending:
time.sleep(180)
return results/v2/data-fields exactly"de" not "DE" or "Germany"minScore too high — lower or remove it when using lookalikeDomains60 req/min, 1,000 req/day (self-serve). See Rate Limiting for backoff code. Check dailyLimitRateLeft from Get Credit Balance before starting large batch jobs.
Acknowledge the webhook right away and offload processing to a background queue. If your handler is slow, Ocean.io will assume failure and retry.
work_queue = Queue()
@app.route("/webhooks/ocean", methods=["POST"])
def receive_webhook():
work_queue.put(request.json)
return jsonify({"status": "ok"}), 200The same payload may arrive more than once. Deduplicate on the record IDs in the payload. Use Redis or a database in production — not an in-memory set.
Include a secret token in the webhook URL and validate it before processing:
https://yourapp.com/webhooks/ocean?secret=MY_SECRET| Operation | Typical |
|---|---|
| Reveal Emails (≤50 IDs) | 1–3 min |
| Reveal Emails (500 IDs) | 2–10 min |
| Enrich batch (≤100 records) | 1–5 min |
| Enrich batch (5,000 records) | 10–30 min |