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

How-to guide

How to Customize B2B Prospecting Software (Filters, Columns and Export Caps)

Customization means you decide the filter, the fields and the volume. Here is the version you can run in one terminal.

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

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.
  • curl and jq.
  • Optional: the hosted MCP server at https://api.leadocean.io/mcp if 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.

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

bash
cat > filters.json <<'EOF'
{
  "jobLevel": ["VP"],
  "jobFunction": ["Sales & Business Development"],
  "employeeRange": ["51-200"],
  "country": ["US"],
  "excludeTitle": ["assistant", "intern"],
  "technologyCategories": ["CRM"]
}
EOF

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

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

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 "$(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:

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

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

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

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

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

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

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

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

ErrorCauseFix
401Missing or wrong x-api-key headerSend the key in the x-api-key header, not as a bearer token. Check $LEADOCEAN_API_KEY is set.
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 recordsThe Free plan has 1,000 records for life. Upgrade: paid plans slow down at their fair-use pace instead of stopping.
Empty dataA bad enum value, such as vp for jobLevelEnums are exact strings. Look up values with GET /v1/enums/{name}.
Export refusedOver 50,000 rows a request or 500,000 a daySplit the list with narrower filters and run it in several exports.
Export refused on FreeBulk CSV export is not on FreeMove 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 →