Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to Build a Fully Custom B2B Lead List (Filters, Exclusions, Columns)

The tool with the best customization is the one where every field is a filter, every filter value is checkable and the output columns are yours. Here is the recipe.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

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=...
  • curl and jq. Requests go to https://api.leadocean.io with the header x-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.

bash
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.

bash
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.

json
{
  "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.

bash
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.

bash
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):

json
{
  "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

ErrorCauseFix
401Missing or wrong x-api-keyCopy the key again from the app. Check $LEADOCEAN_API_KEY is exported.
429Above 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.
402Out of recordsFree is 1,000 records for life. Upgrade or narrow the pull.
Empty resultsA bad enum value, such as Vp or Head of Sales for jobLevelRe-run Step 1 and copy the exact string.
meta.depthCapped is trueOne search reached its 10,000-row page limitAdd filters until the list fits under it.
Export refusedOver 50,000 rows a request or 500,000 a daySplit 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 →