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

How-to guide

How to Segment B2B Data (With Filters, Counts and a Tagged Export)

Define segments as filter objects, size each one for free, then spend records only on the segments you will actually work.

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

B2B data segmentation means splitting one market into groups that get different messages. The reliable way to do it is to write each segment as a filter object, count it for free, and pull rows only for the segments worth working. This guide does that against the LeadOcean API.

TL;DR

  • Write each segment as a flat JSON filter set. Size all of them with free counts before you pull a single row.
  • Takes about 20 minutes. Spends about 75 LeadOcean records (three samples of 25), well inside the 1,000 on the Free plan.
  • Tag every row with its segment name on the way in, so the segment travels with the data into your CRM or sequencer.

Prerequisites

  • A LeadOcean API key from the free plan, stored as $LEADOCEAN_API_KEY. No card needed. Free allows 5 requests a second.
  • curl and python3 with requests installed.
  • Optional: the LeadOcean MCP server at https://api.leadocean.io/mcp, to size segments from Claude without writing code.
  • A rough idea of your ICP: a function, a seniority and a company size band.

Step 1: Write each segment as a filter object

A segment is a named set of filters, nothing more. Keep it in a file so the definition is versioned and the same one drives sizing, pulling and tagging.

Segment on what changes your message. Firmographics (industry, headcount, country) say how the company buys. Role (function, level) says who cares. Add technographics only when the pitch depends on a tool they run.

json
{
  "smb_software_vp_sales": {
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "employeeRange": ["51-200"],
    "industry": ["Software Development"],
    "country": ["US"]
  },
  "midmarket_software_vp_sales": {
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "employeeRange": ["1001-5000"],
    "industry": ["Software Development"],
    "country": ["US"]
  }
}

Save it as segments.json. Two segments that differ by one field are enough to show the pattern. The message to a 100-person company and a 3,000-person company should not match.

Step 2: Check the enum values

Enum filters are exact strings. VP works, vp does not, and a bad value returns an empty result instead of an error. Resolve values before you guess.

bash
curl -s "https://api.leadocean.io/v1/enums" \
  -H "x-api-key: $LEADOCEAN_API_KEY" | head -c 600

The same lookup is free over MCP with leadocean_list_enum_values. Industry has 534 values, so search it by substring (for example software) and copy the string as returned. Software Development and the older Computer Software are separate labels. Job level takes C-Team, VP, Director, Manager, Staff or Other. Headcount takes brackets such as 51-200 and 1001-5000.

Step 3: Size every segment for free

Send count=true as a query parameter with limit 1. The call spends no records and returns the total in meta.total. Do not put count in the JSON body, which rejects unknown fields.

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 '{
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "employeeRange": ["51-200"],
    "industry": ["Software Development"],
    "country": ["US"],
    "limit": 1
  }'

From Claude with the MCP server connected, the same sizing is one tool call to leadocean_count_leads with those filters. For the company side, leadocean_count_companies (GET /v1/companies/count) is also free and returns an exact total with breakdowns by industry, headcount band, HQ country and HQ state.

Read the count as a decision. Under about 100 people, the segment is a hand-written list. In the thousands, it is a sequence. If a segment is too small, drop one filter and count again.

Step 4: Pull a sample page per segment and tag it

Now spend records, but only on a sample. Pull 25 rows per segment, inspect them, then decide whether to pull more. Each person returned is one record, so three samples cost 75.

python
import json, os, requests

API = "https://api.leadocean.io/v1/people/search"
HEADERS = {"x-api-key": os.environ["LEADOCEAN_API_KEY"]}
segments = json.load(open("segments.json"))

rows = []
for name, filters in segments.items():
    body = {**filters, "limit": 25}
    r = requests.post(API, headers=HEADERS, json=body, timeout=30)
    r.raise_for_status()
    for person in r.json()["data"]:
        person["segment"] = name
        rows.append(person)

json.dump(rows, open("segmented_sample.json", "w"), indent=2)
print(len(rows), "rows tagged")

To go past the first page, pass meta.nextCursor from the previous response as cursor and keep the same filters. One search pages to 10,000 rows. When meta.depthCapped is true, narrow the segment instead of paging further.

For a full segment you want as a file, use the Exports page at app.leadocean.io: filter builder, column picker, the record price before you start, then a CSV download. Bulk CSV export is not on Free. It is included on Solo, and Agency allows 50,000 rows a file.

Step 5: Add a third axis only when it earns its place

Firmographic plus role gets you most of the way. Add a technology filter when the message depends on a tool. LeadOcean covers about 7.8M crawled company sites for technographics, so use technologyCategories (105 values, such as Analytics) to split one segment in two.

json
{
  "midmarket_software_vp_sales_crm_users": {
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "employeeRange": ["1001-5000"],
    "industry": ["Software Development"],
    "country": ["US"],
    "technologyCategories": ["CRM"]
  }
}

Count it first. A technology filter only matches companies that were crawled, so the count drops fast. That drop is the point of sizing before pulling.

Step 6: Check segment hygiene before you send

Four checks keep a segment honest. Run them on the sample from Step 4, before you pull more.

  • No overlap. A person must sit in one segment. If two filter sets can match the same row, add an exclusion or dedupe on person_id.
  • Mailable first. Read the email status on each row. Counts default to verified, catch_all_valid and catch_all, and LeadOcean gives no refund for bounced emails.
  • One message per segment. If you cannot write a different first line for two segments, merge them.
  • Refresh monthly. The dataset is refreshed monthly and each record carries its own fetched_at date. Re-count your segments each month, because totals move.

The overlap check is a few lines on the tagged sample:

python
import json

rows = json.load(open("segmented_sample.json"))
seen, clean = {}, []
for r in rows:
    pid = r["meta"]["source_ids"]["person_id"]
    if pid in seen:
        print("overlap:", pid, seen[pid], r["segment"])
        continue
    seen[pid] = r["segment"]
    clean.append(r)

mailable = {"verified", "catch_all_valid", "catch_all"}
keep = [r for r in clean if r["contact_data"].get("email_status") in mailable]
print(len(clean), "unique rows,", len(keep), "mailable")

What you get

Counts below were run on 2026-10-21 with leadocean_count_leads and leadocean_count_companies. Both are free. People totals are mailable people (email status verified, catch_all_valid or catch_all), which is the tool's default.

SegmentFiltersMailable peopleAll matching peopleEmployers (mailable)
smb_software_vp_salesVP, Sales & Business Development, 51-200, Software Development, US1,6494,1411,125
midmarket_software_vp_salesVP, Sales & Business Development, 1001-5000, Software Development, US2,1433,376394

On the company side, leadocean_count_companies for Software Development, 51-200 employees, HQ in US returned 9,261 companies on 2026-10-21. California held 2,398 of them, New York 842 and Texas 776.

The takeaway: both segments are big enough to sequence, but they are shaped differently. The SMB segment is about 1.5 mailable VPs per company, so it is one contact per account. The mid-market segment has about 5 per company, so it suits a multi-threaded account play. Industry labels matter here. The older Computer Software label returned only 46 and 4 mailable people for the same filters. Check which label your market sits under with leadocean_list_enum_values before you trust a small count.

The shape of a search response, from the API reference (placeholder values, not a real record). The segment field is the tag your script adds in Step 4, not an API field.

json
{
  "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",
        "contact_current_experiences": [
          { "company_name": "Acme", "company_domain": "acme.com", "job_title": "VP Sales" }
        ]
      },
      "meta": { "source_ids": { "person_id": "1234567890" } },
      "segment": "smb_software_vp_sales"
    }
  ],
  "meta": { "count": 25, "credits": 25, "nextCursor": "eyJ..." }
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerRe-copy the key into $LEADOCEAN_API_KEY and send it as the x-api-key header.
429Above the plan's rate limit (5 requests a second on Free, 200 on Agency)Honor the Retry-After header, and sleep between segments.
402Out of records (Free is 1,000 for life)Counts stay free. Move to Solo at $97 a month. Paid plans slow at their pace instead of stopping.
Empty data with a nonzero count in your headEnum value is not an exact string, such as vp instead of VPList values with /v1/enums or leadocean_list_enum_values and copy them exactly.
meta.depthCapped is true with no nextCursorOne search stops at 10,000 rowsSplit the segment with another filter (country, headcount band) and run each part.
Export refusedOver 50,000 rows a request or 500,000 rows a daySplit the segment into several exports across days.

FAQ

What are the main types of B2B data segmentation?

Firmographic (industry, size, location), role-based (function and seniority), technographic (tools in use), and behavioral or intent (what they did recently). Firmographic plus role is enough to start. For definitions, see what firmographic data is.

How many segments should I run at once?

Start with three to five. Each one needs its own message, so the limit is how many you can write copy for. Counts tell you which are big enough to be worth it.

Does sizing a segment cost records?

No. Counts, filter lists and enum lookups are free. Records are spent only when a search page, an enrichment or an export returns people. See pricing for the Free, Solo, Agency and Platform plans.

Can I segment on buying intent?

The filters here describe fit, not timing. Segment on fit first, then layer your own signals on top. For that second layer, see AI for intent-based marketing.

Where do I store the segments?

In a versioned file next to your code, and as a segment column in your database. For database structure ideas, see marketing database examples.

Size your first segment for free

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →