Customization in a lead tool comes down to three things: how many fields you can filter on, whether you can exclude as well as include, and whether you control the output columns. Everything else is a menu.
This guide builds one list that uses all three, against the LeadOcean API. It has 50 people filters and 34 company filters, so the example stacks eight of them.
TL;DR
- Resolve exact filter values first, size the list for free, then pull. About 20 minutes.
- Sizing and tuning spend 0 records. The first test page of 25 people spends 25. The export needs a paid plan.
- Exclusions and column choice are what separate a custom list from a filtered one.
Prerequisites
- A LeadOcean account and an API key from app.leadocean.io. Free needs no card.
- The key in your shell:
export LEADOCEAN_API_KEY=... curlandjq. Requests go tohttps://api.leadocean.iowith the headerx-api-key.- Optional: the hosted MCP server at
https://api.leadocean.io/mcp, so an assistant can run Steps 1 and 2 for you.
Step 1: Resolve exact filter values
Enum filters take exact strings, so look the value up instead of guessing. A wrong value returns an empty list, not an error.
curl -s "https://api.leadocean.io/v1/enums" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jq -r '.data.enums[] | "\(.name) \(.count)"'Job level has six values: C-Team, Director, Manager, Other, Staff, VP. Job function has 22, such as Sales & Business Development. Seniority is a separate, finer set in lowercase (c_suite, vp, director). Industry has 534 values, so search it before you pick one. Calls to /v1/enums and /v1/filters cost nothing.
Step 2: Size the list before you pull anything
A count is free, so tune the filters on counts, not on pulled rows. Send count=true as a query parameter with limit=1. The body stays flat JSON with arrays.
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"jobFunction": ["Sales & Business Development"],
"jobLevel": ["VP"],
"country": ["DE"],
"employeeRange": ["51-200", "201-500"],
"technologies": ["HubSpot"],
"limit": 1
}' | jq '.meta'meta.total is capped at 100,000. meta.distinctCompanies tells you how many employers the people span, which is the number to check before you plan account coverage. See predictive lead generation for turning past wins into these filters.
Step 3: Add exclusions and email rules
Exclusion filters are the half of customization most tools skip. Cut out the segments you already work, the roles that never buy, and the addresses that bounce.
{
"jobFunction": ["Sales & Business Development"],
"jobLevel": ["VP"],
"country": ["DE"],
"employeeRange": ["51-200", "201-500"],
"technologies": ["HubSpot"],
"excludeTitle": ["assistant", "intern"],
"excludeIndustry": ["Staffing and Recruiting"],
"excludeCompanyName": ["[Acme]"],
"emailStatus": ["verified", "catch_all_valid"],
"limit": 25
}Square brackets around a company name mean an exact match. Setting emailStatus replaces the default mailable set, so name every status you accept. A count with an email filter is a count of that email status, not of everyone matching the other filters.
Step 4: Pull a test page
Pull one page of 25 and read it before you pull more. This is the first step that spends records.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d @query.json | jq '.meta, (.data | length)'Use limit from 1 to 100 and page with the cursor from the response. One search pages to 10,000 rows. If meta.depthCapped is true, narrow the filters instead of walking further. Add a second filter, not a bigger page.
Step 5: Choose the columns and export
The export is where column control lives. The body takes filters (the Step 3 filter set, without limit), a row limit, and either a preset or your own columns in order. Numbered contact columns are requested as email_N_address, email_N_status and phone_N_number, and caps sets how many of each (defaults: 3 emails, 2 phones). List every column id with the free GET /v1/exports/columns.
curl -s -X POST "https://api.leadocean.io/v1/exports" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"name": "de-sales-vps-hubspot",
"filters": {
"jobFunction": ["Sales & Business Development"],
"jobLevel": ["VP"],
"country": ["DE"],
"employeeRange": ["51-200", "201-500"],
"technologies": ["HubSpot"],
"excludeTitle": ["assistant", "intern"],
"emailStatus": ["verified", "catch_all_valid"]
},
"limit": 100,
"columns": ["profile_full_name", "current_job_title", "current_company_name",
"current_company_domain", "email_N_address", "email_N_status"],
"caps": { "emails": 1 }
}' | jq '.data | {id, status, requested}'The CSV headers come back numbered: email_1_address, email_1_status. Every row costs one record whatever the columns. An export takes 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 the same flow with a filter builder, a column picker, a price preview and a CSV download. It does not sequence or send: that part is your tool.
What you get
This is a live free count run through the MCP server on 2026-10-21, using the Step 2 filters (VP, Sales & Business Development, Germany, 51-500 employees, HubSpot on the website):
{
"total": 97,
"totalIsExact": true,
"totalAll": 289,
"distinctCompanies": 75,
"defaults": "Counted only addresses worth sending to (verified or catch_all_valid or catch_all)"
}97 people have a mailable address, out of 289 who match every other filter, across 75 companies. The cost was 0 records. Stack one more filter and the number falls; loosen one and it rises. That feedback loop, on free counts, is the customization.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key | Copy the key again from the app. Check $LEADOCEAN_API_KEY is exported. |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After, or move to a plan with a higher rate. |
| 402 | Out of records | Free is 1,000 records for life. Upgrade or narrow the pull. |
| Empty results | A bad enum value, such as Vp or Head of Sales for jobLevel | Re-run Step 1 and copy the exact string. |
meta.depthCapped is true | One search reached its 10,000-row page limit | Add filters until the list fits under it. |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the export by country or headcount band. |
FAQ
Which filters matter most for a custom list?
Job function plus job level, headcount and country do most of the work. Add technologies and exclusions last, once the count looks right.
Can I filter by tools a company uses?
Yes. technologies matches tools detected on the company website, and technologyCategories matches what the tool is for. Coverage is about 7.8M crawled sites.
Do counts cost records?
No. A count, a filter lookup and an empty page cost nothing. Only people or companies you receive count.
Can an AI assistant do this for me?
Yes, through the 13 MCP tools. leadocean_count_leads and leadocean_list_enum_values are free, so an assistant can tune a list without spending anything. Read more on outbound lead generation and the lead generation funnel, or see pricing.
Build your custom list on free counts
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →