The most customizable prospecting software is the one where every field is a parameter you control. You choose the filters, the output columns and the row count, and nothing is locked behind a workspace UI. This guide builds one custom list end to end against the LeadOcean API.
TL;DR
- You will define a filter set, size it for free, pick your own export columns and numbered caps, then pull a CSV.
- It takes about 20 minutes. On Free, only the test page of 25 spends records. The 500-row export needs Solo or higher and spends 500.
- Customization here lives in request bodies you can version in git, not in a settings screen.
Prerequisites
- A LeadOcean API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. The Free plan works for every step except bulk CSV export, which needs Solo ($97 a month) or higher. curlandjq.- Optional: the hosted MCP server at
https://api.leadocean.io/mcpif you want an AI client to run the same sizing calls. It has 13 tools.
Step 1: Look up the exact filter values
Closed-list filters fail on a typo, so resolve values first. Lookups are free.
curl -s "https://api.leadocean.io/v1/enums/employee_range" \
-H "x-api-key: $LEADOCEAN_API_KEY"The same lookup through MCP is leadocean_list_enum_values with name: "employee_range". Job level values are C-Team, VP, Director, Manager, Staff and Other. Seniority is lowercase (c_suite, vp, director).
For the full list of 50 people filters and 34 company filters, call GET /v1/filters.
Step 2: Write the filter set as flat JSON
A search body is flat JSON. Every filter is an array. Keep it in a file so the list is reproducible.
cat > filters.json <<'EOF'
{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"employeeRange": ["51-200"],
"country": ["US"],
"excludeTitle": ["assistant", "intern"],
"technologyCategories": ["CRM"]
}
EOFEach line is a lever. technologyCategories filters on what the company site runs (CRM, Analytics, Ecommerce), and it covers the about 7.8M crawled sites, not the whole database. excludeTitle removes noise. Stack filters until the list is yours.
Step 3: Size the list before you spend anything
Send count=true as a query parameter with limit=1. The body stays your filter file.
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d @filters.json | jq '.meta'meta.total is capped at 100,000, so a result at the cap is a floor. If the total is wrong for your motion, edit filters.json and run it again. Sizing is free, however many times you do it.
Step 4: Check the shape with one page of 25
Pull a single page to confirm the fields are what you expect. Limit runs from 1 to 100, and you paginate with cursor.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq '. + {limit: 25}' filters.json)" | jq '.meta'This spends 25 records. One search pages to 10,000 rows. When meta.depthCapped is true there is no nextCursor, so narrow the filters instead of walking the index.
Step 5: Choose your columns and caps
The export is where you customize the output. List every column id first:
curl -s "https://api.leadocean.io/v1/exports/columns" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jq '.'Then request only what you need. Two presets exist: search (27 columns, no contact points) and full (45 columns). columns overrides the preset, and caps sets how many numbered blocks you get (emails 3 by default, phones 2).
EXPORT_ID=$(curl -s -X POST "https://api.leadocean.io/v1/exports" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --slurpfile f filters.json '{
name: "us-vp-sales-51-200",
filters: $f[0],
limit: 500,
columns: ["profile_full_name", "current_job_title", "current_company_domain",
"email_N_address", "email_N_status", "phone_N_number"],
caps: {emails: 1, phones: 1}
}')" | jq -r '.data.id')Numbered columns are requested as email_N_address, and caps decides how many come back. An export takes up to 50,000 rows a request and 500,000 a day. Every row costs one record whatever the columns, so trimming columns shortens the file but not the bill. You can do the same in the app: the Exports page has a filter builder, a column picker with presets and a price preview before you start.
Step 6: Download the file
The create call returned the export id in data.id. Poll it until status is done, then download.
curl -s "https://api.leadocean.io/v1/exports/$EXPORT_ID" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jq '.data.status'
curl -s -o list.csv "https://api.leadocean.io/v1/exports/$EXPORT_ID/download" \
-H "x-api-key: $LEADOCEAN_API_KEY"CSV headers come back as email_1_address, email_1_status and phone_1_number. Read email_1_status before you send anything. LeadOcean gives no refund for bounced emails.
Step 7: Customize from the company side
Sometimes the account list comes first and the people come second. Size a company list with /v1/companies/count. It is free, takes the company-search filters, and returns an exact total with breakdowns by industry, headcount band, HQ country, HQ state and NAICS.
curl -s -X POST "https://api.leadocean.io/v1/companies/count" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"employeeRange": ["51-200"], "hqCountry": ["US"], "technologyCategories": ["CRM"]}' \
| jq '.data.total, .data.facets.industry[:5]'Then pull the people at each account with GET /v1/companies/{domain}/people, which stops at 10,000 rows. For one person, POST /v1/people/enrich takes exactly one of linkedin_url, email, phone or person_id. Since October 2026 it also returns career history: past roles, education, certifications and awards. Past employers carry no company_id yet, so match them by name.
What you get
Real output from the free enum lookup, run through the MCP tool leadocean_list_enum_values on 2026-10-21:
{"name":"employee_range","total":8,"matched":8,"values":["1-10","11-50","51-200","201-500","501-1000","1001-5000","5001-10000","10001+"]}Use these strings as written.
And the CRM technology category, same date:
{"name":"tech_category","matched":1,"values":["CRM"],"labels":{"CRM":"CRM (35,416 companies)"}}That is the exact string the filter accepts.
The category filter searches the crawled slice only. The tool says its counts come from the 2026-09-07 crawl, so treat them as a snapshot.
The Step 2 filter set, sized with the free leadocean_count_leads tool on 2026-10-21.
{"total": 40, "totalIsExact": true, "totalAll": 134, "distinctCompanies": 30, "credits": 0}40 people have a mailable email (verified, catch_all_valid or catch_all), out of 134 who match the other filters, at 30 companies. A list this narrow is the point of stacking filters.
A people search response has this shape (response shape from the API reference, placeholder values):
{
"success": true,
"data": [
{
"profile_data": { "profile_full_name": "Jane Doe" },
"contact_data": {
"email_status": "verified",
"contact_current_experiences": [
{ "job_title": "VP Sales", "company_name": "Acme", "company_domain": "acme.com" }
]
},
"meta": { "source_ids": { "person_id": "p_0000000" }, "fetched_at": "2026-10-01" }
}
],
"meta": { "count": 1, "nextCursor": null, "depthCapped": false }
}Expect more fields per row than shown here.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Send the key in the x-api-key header, not as a bearer token. Check $LEADOCEAN_API_KEY is set. |
| 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 | The Free plan has 1,000 records for life. Upgrade: paid plans slow down at their fair-use pace instead of stopping. |
Empty data | A bad enum value, such as vp for jobLevel | Enums are exact strings. Look up values with GET /v1/enums/{name}. |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the list with narrower filters and run it in several exports. |
| Export refused on Free | Bulk CSV export is not on Free | Move to Solo, or page the search API instead. |
FAQ
Can I change which fields come back?
Yes. Pass columns with ids from /v1/exports/columns, or pick a preset (search or full). Numbered blocks such as emails and phones take a cap from 1 to 10.
Does trimming columns lower the cost?
No. Every exported row costs one record whatever columns you pick. Smaller files are faster to load, but the record count is the same.
Is there a UI for this?
Yes, but only for exports. The app has a filter builder, a column picker and a price preview. LeadOcean has no sequencer, dialer, CRM or Chrome extension, so the list goes into the tool you already use.
Can an AI client run the sizing step?
Yes. The hosted MCP server has leadocean_count_leads and leadocean_count_companies, both free. Sign in with OAuth, no key pasted.
See how a rep uses a list like this in rep prospecting. Read the integration side of the same question. Turn a good list into more like it with lookalike prospecting. Plan details are on pricing.
Customize your prospect list from the first request
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →