Data enrichment is adding fields you do not have (email, phone, title, company size) to a record you do. You give one identifier. You get back the rest. This guide shows each step against the LeadOcean API.
TL;DR
- Enrichment fills gaps in records you already hold. Search finds records you do not hold yet. Use both, in that order.
- The flow takes about 20 minutes: size the list for free, search, enrich by
person_id, then checkemail_statusbefore you send. - On the Free plan it spends 1 to 5 of your 1,000 records. Counts and enrichments that find nothing cost nothing.
Prerequisites
- A LeadOcean account. The Free plan has 1,000 records for life, no card, 5 requests a second.
- An API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. curlandjq, or any HTTP client.
Define the terms first. Enrichment is lookup by a known key (LinkedIn URL, email, phone or person_id). Search is discovery by filters. Cleansing fixes records you already have. Enrichment adds to them.
Most guides stop at the definition. A buyer who is technical wants the request and the response. Here they are.
Step 1: Size the list before you spend anything
Count first. A count is free on every plan, so you learn whether the list exists before you pull a row.
curl -s "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["CA"]}'count=true goes in the query string, not the JSON body. The body takes flat filters, each an array of strings. meta.total is capped at 100,000.
The same count is available as the MCP tool leadocean_count_leads. Ask your assistant: "How many VP-level sales people are in Canada?"
Step 2: Search for the records you do not have
Search returns people matching your filters. Each person returned is one record.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["CA"],"limit":3}' \
> results.json
jq -r '.data[].meta.source_ids.person_id' results.jsonUse limit from 1 to 100 and page with cursor. One search pages to 10,000 rows. When meta.depthCapped is true, narrow the filters instead of walking further.
Enum values are exact strings. If you are unsure of one, call leadocean_list_enum_values or GET /v1/enums. Both are free.
Step 3: Enrich one record by person_id
Enrichment takes exactly one of linkedin_url, email, phone or person_id. There is no name plus domain lookup. Search by domain first, then enrich by person_id.
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"person_id":"PERSON_ID_FROM_STEP_2","reveal_email":true}'reveal_email defaults to false. Set it to true to get the address, at no extra record. A person we hold nothing for returns 404 and costs nothing.
Since October 2026, person enrich also returns career history: past roles, education, certifications and awards. Past employers carry no company_id yet.
Step 4: Enrich the company
Company enrichment works the same way, keyed on a domain.
curl -s -X POST https://api.leadocean.io/v1/companies/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"acme.com"}'To list the people at a company, use GET /v1/companies/acme.com/people. It stops at 10,000 rows. To size a company list for free, use GET /v1/companies/count or leadocean_count_companies.
Step 5: Check email_status before you send
An address is not a verified address. Read email_status on every row.
jq '.data[] | select(.contact_data.email_status == "verified" or .contact_data.email_status == "catch_all_valid")' results.jsonThe count tool counts verified, catch_all_valid and catch_all by default. A count with hasEmail or emailStatus is not the same as a mailable count, so label what you filtered. There is no refund for bounced emails.
For many rows at once, use POST /v1/exports: up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free. The app at app.leadocean.io has an Exports page with a filter builder, a column picker and a price preview.
What you get
Count run on 2026-10-21 with leadocean_count_leads, filters jobLevel VP, jobFunction Sales & Business Development, country CA:
{
"total": 5574,
"totalIsExact": true,
"totalAll": 18800,
"distinctCompanies": 3873
}total is the mailable audience (verified, catch_all_valid or catch_all). totalAll is the same filters with no email condition. The count cost 0 records.
Enrich response shape from the API reference, with placeholder values:
{
"success": true,
"data": {
"profile_data": { "profile_full_name": "Jane Doe" },
"contact_data": {
"has_email": true,
"email_status": "verified",
"contact_emails": [
{ "email": "jane.doe@acme.com", "type": "work", "status": "verified" }
],
"contact_phones": [{ "phone": "+1 415 555 0133", "type": "mobile" }],
"contact_current_experiences": [
{ "job_title": "VP Sales", "company_name": "Acme", "company_domain": "acme.com" }
]
},
"resume_data": { "experiences": [], "educations": [] },
"meta": { "fetched_at": "2026-10-01", "schema": "leadocean.person.v1" }
}
}contact_emails and contact_phones appear only when reveal_email is true. A field we do not hold comes back null.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key | Check $LEADOCEAN_API_KEY is exported and sent as the x-api-key header |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Back off for the Retry-After seconds, or move to a plan with a higher limit |
| 402 | Out of records (Free is 1,000 for life) | Check GET /v1/usage, then upgrade or narrow the pull |
| Empty results | A bad enum value, such as VP typed as Vice President | Look up the exact string with GET /v1/enums or leadocean_list_enum_values |
| 404 on enrich | We hold nothing for that identifier | Nothing was charged. Try another identifier, or search by domain first |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the export by filter, or by day |
FAQ
What is the difference between data enrichment and a data list?
A list gives you new records. Enrichment adds fields to records you already hold. Buy a list when you have no one to start from. Enrich when you have a CRM full of thin rows.
Does enrichment cost a record when it finds nothing?
No. An enrichment that finds nothing returns 404 and costs nothing. A person or company returned costs one record.
What is waterfall enrichment, and does LeadOcean use it?
Waterfall enrichment queries several providers in sequence and stops at the first match, as FullEnrich describes on its page (September 2026). LeadOcean is one database with one record per person, queried directly. You choose the identifier and read email_status yourself.
How fresh is the data?
The dataset is refreshed monthly. Each record also carries its own fetched_at date.
Can I run this from an AI assistant?
Yes. The hosted MCP server at https://api.leadocean.io/mcp has 13 tools, including free counting and filter lookup. Sign-in is OAuth, so no key is pasted.
Related reading: What is data enrichment, What data enrichment actually costs, A CLI for B2B data enrichment and pricing.
Enrich your first records with a free API key
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →