A quality lead is a person who matches your buyer profile and has an email address you can read a status on. A database earns its price by letting you check both before you spend anything.
Most guides on this topic describe what a database is. This one runs the workflow: size the list, search it, filter on email status, enrich the best rows and export the rest.
TL;DR
- Size your target list for free with a count, then search only when the number looks right.
- Filter on
emailStatusat search time, so you never pay for an address you will not mail. - The walkthrough takes about 20 minutes and spends about 26 LeadOcean records. A Free account holds 1,000.
Prerequisites
- A LeadOcean account. The Free plan is $0 for 1,000 records, no card, 5 requests a second.
- An API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. curlandjq. Optional: Claude or another MCP client pointed athttps://api.leadocean.io/mcpfor the free count tools.- A written buyer profile: job level, job function, country and company size. Four filters are enough to start.
export LEADOCEAN_API_KEY="your-key-here"
curl -s https://api.leadocean.io/v1/account -H "x-api-key: $LEADOCEAN_API_KEY"GET /v1/account is free. It confirms the key works and shows what you have left.
Step 1: Write the buyer profile as filters
Turn the profile into filter values before you touch the data. Filters are arrays of exact strings, so a typo returns nothing.
List the allowed values for the filters you plan to use. Both calls are free.
curl -s "https://api.leadocean.io/v1/enums" -H "x-api-key: $LEADOCEAN_API_KEY"
curl -s "https://api.leadocean.io/v1/filters" -H "x-api-key: $LEADOCEAN_API_KEY"Our example profile: VPs of sales in the US.
{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["US"]
}Use jobLevel and jobFunction over free-text title when you want a clean segment. Titles vary ("VP Sales", "Vice President, Revenue"). The enums do not.
Step 2: Size the list before you search
Count first. A count tells you whether the segment is too broad, too narrow or just right, and it costs nothing.
Over the API, send count=true as a query parameter with limit=1. The filters go in the request, the total comes back in meta.total.
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": ["US"]
}'meta.total is capped at 100,000 on this call. The MCP tool leadocean_count_leads runs the same count from Claude and adds facets by industry and company size.
Company lists have their own free sizing call, GET /v1/companies/count. It takes the company-search filters and returns an exact total with breakdowns by industry, headcount band and HQ country.
Step 3: Search with email status as a filter
Search returns rows. Each person returned costs one record, so narrow the filters until the count is a list you would actually work.
Add emailStatus and a company size band. Pull one page of 25 to check quality before you pull more.
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": ["US"],
"employeeRange": ["51-200", "201-500"],
"emailStatus": ["verified"],
"limit": 25
}'The default for mailable counts is verified, catch_all_valid and catch_all. Asking for verified only gives you the strictest list. Pick the looser default when volume matters more than bounce rate.
Page with meta.nextCursor and send it back as cursor. One search pages to 10,000 rows. When meta.depthCapped is true, narrow the filters and run a second search instead of paging harder.
Step 4: Enrich the rows worth the effort
Enrich the handful of leads you will contact by hand. Send exactly one of linkedin_url, email, phone or person_id.
Search rows carry the id at meta.source_ids.person_id. Use it.
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": "1234567890", "reveal_email": true }'Enrichment returns the current role and, since October 2026, career history: past roles, education, certifications and awards. That is enough for a first line that is not "I saw your profile". Past employers have no company_id yet, so you cannot join them to the company table.
An enrichment that finds nobody is a 404 and costs nothing. Phone numbers come from GET /v2/people/phone, which costs one record per call even when it finds nothing.
Step 5: Export the full list
Export when the list is final. POST /v1/exports takes the same filters, a limit and a column preset, and spends one record per row written.
curl -s -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"name": "us-vp-sales-51-500",
"filters": {
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["US"],
"employeeRange": ["51-200", "201-500"],
"emailStatus": ["verified"]
},
"limit": 5000,
"preset": "full"
}'An export is asynchronous. Poll GET /v1/exports/{id}, then fetch GET /v1/exports/{id}/download. The ceilings are 50,000 rows a request and 500,000 a day.
Bulk CSV export is not on Free. It is included on Solo ($97 a month). Agency ($497 a month) allows 50k rows a file. You can also build the same export in the app: filter builder, column picker and a price preview before you start. See pricing.
What you get
This is the free count for the profile in Step 1, run through leadocean_count_leads on 2026-10-21. It counts mailable addresses (verified, catch_all_valid or catch_all), the tool's default.
{
"total": 129494,
"totalIsExact": true,
"totalAll": 350517,
"distinctCompanies": 58121,
"facets": {
"company_size": [
{ "value": "10001+", "count": 23661 },
{ "value": "11-50", "count": 20836 },
{ "value": "1001-5000", "count": 19438 },
{ "value": "51-200", "count": 18567 }
]
}
}Read it this way: 350,517 US sales VPs match, and 129,494 of them have an address worth sending to. The gap is the point of filtering on status.
A search row follows this shape. It is the response shape from the API reference (openapi.json, October 2026), with placeholder values.
{
"success": true,
"data": [
{
"profile_data": {
"profile_full_name": "Jane Doe",
"profile_headline": "VP Sales at Acme"
},
"contact_data": {
"has_email": true,
"email_status": "verified",
"email_type": "work"
},
"meta": { "source_ids": { "person_id": "1234567890" } }
}
],
"meta": { "count": 1, "limit": 25, "nextCursor": "CURSOR", "depthCapped": false }
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Re-export $LEADOCEAN_API_KEY and check the header name. |
| 429 | More requests a second than the plan allows (5 on Free, 50 on Solo, 200 on Agency) | Honor Retry-After, or slow the loop. |
| 402 | Out of records | Check GET /v1/account. Free holds 1,000 records for life. |
| Empty results | A filter value that is not an exact enum string, such as vp for jobLevel | Look up the value in /v1/enums. jobLevel is VP; seniority is lowercase vp. |
400 on count=true in the body | The JSON body rejects unknown fields | Send count=true as a query parameter. |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the export by country or industry and run it across days. |
FAQ
How many leads should I pull on the first run?
One page of 25. Check email status, titles and company fit on those rows, then widen. Twenty-five records is a cheap way to find a bad filter.
Is a count the same as a mailable list?
No. A count with emailStatus is a count of rows with that status, not a promise of inbox delivery. Read email_status on every row before you send, and note that bounced addresses are not refunded.
How fresh is the data?
The dataset is refreshed monthly. Each record also carries its own fetched_at date, so you can drop old rows yourself.
Can I do this without writing code?
Yes. The app has an Exports page with a filter builder and a price preview. Or ask Claude through the MCP server to run the counts. There is no sequencer or CRM in the app, so the CSV goes into the tools you already use.
Which database is best for this?
That depends on whether you need an API. See the honest comparison of B2B lead databases and the best lead database guide. For keeping lists current, read real-time lead data for your pipeline. More guides are in the blog.
Size your first lead list for free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →