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

How-to guide

How to Validate B2B Data (5 Techniques With Real Queries)

Five checks you can run against a people API before a list reaches your CRM or sequencer. Real requests, real counts, the response shape.

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

Validate B2B data in five checks: confirm the filter values, size the list for free, gate on email status, read the check date, and cross-check identity. Each check below is one request against LeadOcean. The whole run spends 7 records.

Examples use placeholders (Acme, acme.com, Jane Doe). Swap in your own domain and filters.

TL;DR

  • Validate before you spend. Enum lookups and counts are free, so wrong filters cost nothing to catch.
  • Never send on a status you did not choose. Filter on emailStatus and emailType in the query, then read verified_batch_date per address.
  • Takes about 20 minutes and spends 7 records of the 1,000 free ones.

Most validation advice is a checklist: verify emails, remove duplicates, standardize fields. That is correct and hard to act on. A technical buyer wants the request, the field to read and the number that comes back. This guide gives you those.

The order matters. Free checks come first (Steps 1 and 2), paid checks run on a sample (Steps 3 to 5). You never pay to learn that a filter was wrong.

Prerequisites

  • A LeadOcean account and a key from app.leadocean.io. The Free plan works: no card, 1,000 records for life.
  • The key in your shell: export LEADOCEAN_API_KEY=...
  • curl and jq.

All requests go to https://api.leadocean.io with the header x-api-key.

Step 1: Validate the filter values

Bad filter values do not error. They return nothing, or the wrong list. Enum values are exact strings, so fetch the allowed ones first.

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

Do the same for job_level, seniority and country. VP is a job level. vp is a seniority. Mixing them is the most common empty-result bug. This call is free, and so is leadocean_list_enum_values if you work through the MCP server.

Step 2: Size the list before you pull it

A count tells you whether the filter means what you think. Send count=true as a query parameter with limit=1. It returns no rows and costs nothing.

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 '{"jobLevel":["VP"],"country":["US"]}' | jq '.meta | {total, totalIsExact, distinctCompanies}'

Two checks on the answer. First, totalIsExact must be true, or the number is a floor. REST meta.total stops at 100,000, so a list this wide comes back as a floor; the free MCP tool leadocean_count_leads returns the exact total. Second, distinctCompanies tells you how many employers the people span. A list of 1.2M people across 226K companies is a different list from the same people across 20K.

If totalWithheld appears, a filter was not applied. Fix the query. Do not quote the number, and do not size a campaign on it.

Step 3: Gate on email status in the query

Email status is the cheapest validation there is, because you apply it before you pay for a row. Ask for the status you will accept and the address type you want.

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 '{
    "jobLevel": ["VP"],
    "country": ["US"],
    "emailStatus": ["verified"],
    "emailType": ["work"],
    "limit": 5
  }'

emailType: work means the address is at the person's current employer. work_other is usually a former employer's mailbox, and it is the address most likely to bounce. Ask for it on purpose or not at all.

Page through the rest with meta.nextCursor. Pass the string back unchanged. A cursor you edit or reuse on a different search is refused with a 400.

Pick the status by risk. verified is the strictest. The default count also includes catch_all_valid and catch_all. A catch-all domain accepts any address, so the check cannot prove the mailbox exists. LeadOcean does not refund bounces, so read the status before you send.

Step 4: Read the check date, not just the status

A status without a date is half a validation. Enrich one person from Step 3 with person_id, which a search row carries at meta.source_ids.person_id.

bash
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"person_id":"1234567890","reveal_email":true}' \
  | jq '.data.contact_data.contact_emails[] | {email, type, status, verified_batch_date}'

Each address carries status and verified_batch_date. A date means LeadOcean ran the check. A null date means the status came from a source held to the same standard, not from our own run. Treat null as a signal to re-verify before a large send, not as a failure. Some verified addresses carry a date and some do not, so sort your sample by it and look at the split.

The date is a batch date, not a timestamp for that address. The dataset also refreshes monthly, and each record has its own fetched_at.

Step 5: Cross-check identity from the other direction

Run the lookup backwards. If you already hold an email from your CRM, ask who owns it and compare.

bash
curl -s "https://api.leadocean.io/v2/people/reverse?email=jane.doe@acme.com" \
  -H "x-api-key: $LEADOCEAN_API_KEY"

The response gives the person who holds that address, plus the address's own type, status and check date. If the name or employer disagrees with your CRM row, one of them is stale. A 404 means no person holds the address, and that is a finding too. It costs one record per call, found or not.

Use this on a sample, not on the full list. Fifty rows from a 5,000-row import tell you the error rate. Track two numbers: the share where the employer differs, and the share where the status is not verified. If either is above what you can tolerate, re-pull the list with the Step 3 gate instead of patching rows.

What each check catches

StepCatchesCost
1. Enum lookupFilter values that match nothingFree
2. Free countA list that is the wrong size or widthFree
3. Status gateUndeliverable and former-employer addresses1 record per row returned
4. Check dateStatuses from a source other than our own run1 record
5. Reverse lookupStale CRM rows that point at the wrong person1 record

What you get

Counts below were run on 2026-10-21 with the free leadocean_count_leads tool for jobLevel VP in the US. No rows were returned. The default mailable count spans about 226,527 employers (an estimate above 40,000).

FilterPeople
No email filter3,102,417
Default mailable (verified, catch_all_valid, catch_all)1,242,801
emailStatus: verified only257,840

The same 3.1M people shrink to 258K when you accept only verified. That gap is the validation decision. Know which number your list is built on.

Response shape from the API reference, placeholder values:

json
{
  "success": true,
  "data": {
    "contact_data": {
      "has_email": true,
      "email_status": "verified",
      "email_type": "work",
      "contact_emails": [
        {
          "email": "jane.doe@acme.com",
          "type": "work",
          "status": "verified",
          "verified_batch_date": "2026-09-20"
        }
      ]
    }
  }
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerRe-export $LEADOCEAN_API_KEY and check the header name
429Above the plan's rate limit: 5 requests a second on Free, 200 on AgencyWait for the Retry-After value, then retry
402The 1,000 free records are spentUpgrade to Solo ($97 a month) or Agency ($497 a month)
Empty resultsA filter value is not an exact enum string, such as vp in jobLevelList the values with /v1/enums/job_level and copy one
totalWithheld in the countA filter could not be appliedNarrow the query and count again
meta.depthCapped is trueOne search pages through 10,000 rows at mostSplit the search into narrower filters

FAQ

What is B2B data validation?

It is checking that a record is correct, complete and current before you act on it. For contact data that means a deliverable email, the right employer and a recent check date.

How many records does validation spend?

This guide spends 7. Enum lookups, counts and sizing calls are free. A search row, an enrichment and a reverse lookup each cost one record.

Is verified a guarantee the email will not bounce?

No. It means the address passed a check, run by our verifier or by a source we hold to the same standard. Mailboxes change after the check, which is why Step 4 reads the batch date.

Should I validate again after a month?

Yes. The dataset refreshes monthly, and addresses decay. Re-run Steps 2 and 3 on a saved filter before each large send. For the wider audit, see the B2B data quality guide.

Which plan do I need?

Free covers this guide. Solo at $97 a month suits one person validating regularly. See pricing for Agency and Platform.

Validate your list before it reaches your CRM

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

Get your free API key →