B2B data quality is four measurable things: fit, reachability, freshness and duplication. Most guides stop at "verify your data". This one gives the queries, so you can score any list or any provider in under an hour.
TL;DR
- Score a segment on four numbers: how many records match, how many have a sendable email, how old the verification is, and how many are duplicates.
- Takes 30 to 45 minutes. Counts are free. One sample page of 25 people and two enrichments spend about 27 records, out of the 1,000 on the Free plan.
- Send only to
verifiedandcatch_all_validfirst. Treatrisky,unknownanduntestedas a separate, smaller test batch.
Prerequisites
- A LeadOcean account. The Free plan is enough: 1,000 records for life, no card, 5 requests a second.
- An API key in your shell:
export LEADOCEAN_API_KEY=... curlandjq. Optional: the hosted MCP server athttps://api.leadocean.io/mcp, which uses OAuth sign-in and needs no key.- A segment in mind. Example here: VPs in the United States.
Step 1: Define fit and size it for free
Fit is whether the people in the list match your buyer. Size the segment first, because every later number is a share of it.
Use the free count. In the REST API, send count=true as a query parameter and limit: 1 in the body. Over MCP, call leadocean_count_leads.
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{"jobLevel":["VP"],"country":["US"],"limit":1}' | jq '.meta | {total, totalIsExact, distinctCompanies}'Filter values are exact strings. Check them with leadocean_list_enum_values or GET /v1/enums/job_level before you trust an empty result.
Step 2: Measure reachability by email status
Reachability is the share of the segment you can email without bouncing. Count the segment twice: once with LeadOcean's default mailable set, once with verified only.
The default counts verified, catch_all_valid and catch_all. Passing emailStatus replaces that default, and passing every value returns the unfiltered total.
echo "mailable default: verified, catch_all_valid, catch_all"
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{"jobLevel":["VP"],"country":["US"],"limit":1}' | jq '.meta.total'
echo "verified only"
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{"jobLevel":["VP"],"country":["US"],"emailStatus":["verified"],"limit":1}' | jq '.meta.total'A count with emailStatus is not a mailable count of the whole list. Label it by what you filtered. The 13 status values are in GET /v1/enums/email_status.
Step 3: Check freshness on a sample
Freshness is how recently the record and its email were checked. Pull one page of 25, then enrich two of the rows with reveal_email to read the dates.
curl -s "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"],"limit":25}' \
| jq '.meta.nextCursor, (.data | length)'The dataset is refreshed monthly, and each record carries its own fetched_at date. On an enriched email, verified_batch_date is the date of the verification run. A null there means LeadOcean did not run the check itself. Ask any provider the same question.
For a person who changed jobs, enrich returns career history (resume_data) so you can see whether the current role matches the title you searched.
Step 4: Test duplication and concentration
Duplication is how many rows point at the same person or the same company. The people count carries meta.distinctCompanies, the number of employers the matched people span.
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{"jobLevel":["VP"],"country":["US"],"limit":1}' \
| jq '.meta | {people: .total, employers: .distinctCompanies, per_employer: (.total / .distinctCompanies)}'If one employer holds dozens of matches, you are buying a few accounts many times. Size the company side for free with GET /v1/companies/count, or the MCP tool leadocean_count_companies. Then pull by account, not by title.
Step 5: Spot-check records and set your gate
A spot check is 25 real rows, read by a human. Look for title drift, wrong countries and role mailboxes. Then write down a gate.
Send gate (example, tune to your tolerance)
- email_status in (verified, catch_all_valid) -> send
- email_status in (catch_all, risky) -> small test batch, watch bounces
- email_status in (unknown, untested) -> do not send
- email_status in (invalid, role, disposable,
spam_trap, abuse) -> dropLeadOcean gives no refund or credit-back for bounced emails, so the gate is yours to enforce. Read email_status before every send.
What you get
Free counts from leadocean_count_leads, run on 2026-10-21 for jobLevel VP, country US:
{
"default_mailable": { "total": 1242801, "totalIsExact": true, "totalAll": 3102417, "distinctCompanies": 226527 },
"verified_only": { "total": 257840, "totalIsExact": true, "distinctCompanies": 83722 }
}Read it like this: of 3,102,417 US VPs, 1,242,801 are in the default mailable set (about 40%), and 257,840 are verified (about 8%). The first number is the list you could mail. The second is the list you can trust most. Both cost 0 records.
An enriched record carries the dates. Response shape from the API reference (placeholder values):
{
"success": true,
"data": {
"contact_data": {
"has_email": true,
"email_status": "verified",
"contact_emails": [
{ "email": "jane.doe@acme.com", "type": "work", "status": "verified", "verified_batch_date": "2026-09-20" }
]
}
},
"meta": { "source": "leadocean" }
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key | Re-export $LEADOCEAN_API_KEY and resend |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After, or batch the sizing calls |
| 402 | Out of records (Free is 1,000 for life) | Counts stay free. Move to Solo at $97 a month for more |
| Empty results | Bad enum value, such as vp for jobLevel | Use exact strings from GET /v1/enums: VP for jobLevel, vp for seniority |
meta.total stops at 100,000 | The count cap | Narrow the filters until the total is exact |
meta.depthCapped is true | One search pages to 10,000 rows | Split into smaller searches by country or industry |
FAQ
What is a good email verification rate?
Set it by status, not by a single percent. Send to verified and catch_all_valid first, test catch_all and risky in small batches, and drop the rest. LeadOcean publishes no bounce guarantee, so your own bounce rate on the first 200 sends is the real test.
How often should I re-check B2B data?
Re-check before every large send and at least monthly. LeadOcean's dataset is refreshed monthly, and each record carries its own fetched_at date. Compare it with the date of your send.
Does a catch-all domain mean the email is bad?
No. A catch-all server accepts any address, so the verifier cannot confirm the mailbox. catch_all_valid is a stronger signal than catch_all. Treat both as a test batch.
Can I run this audit over MCP instead of curl?
Yes. leadocean_count_leads, leadocean_list_filters and leadocean_list_enum_values are free. Ask your assistant to count the same segment with and without emailStatus: verified. See pricing for plan limits and the data validation techniques guide for the wider checklist.
What does tech data quality look like?
The same four checks apply to technographic data: coverage, freshness, duplicates and fit. See our Wappalyzer review for how one tech-detection source scores. For background, start with what B2B data is.
Score your own list before you spend a record
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →