TL;DR
- Firmographic errors come from four places: estimated fields, missing values, loose industry labels and stale records. Each has a check you can script.
- The whole run takes 30 to 45 minutes and spends about 10 records. The sizing steps are free.
- The rule that fixes most of it: filter on brackets and labels you have verified, then sample-check before you commit a list.
Prerequisites
- A LeadOcean account. The Free plan gives 1,000 records for life, no card.
- An API key in your shell as
$LEADOCEAN_API_KEY. curlandjq.- Optional: the LeadOcean MCP server at
https://api.leadocean.io/mcp, to run the free count tools from an agent.
Step 1: Size the segment before you trust it
Start with a free count. It shows how big the segment is and how the data splits, and it spends nothing.
curl -s -G "https://api.leadocean.io/v1/companies/count" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "industry=Software Development" \
--data-urlencode "hqCountry=US" \
--data-urlencode "employeeRange=51-200" | jq '.data.total'The response also carries breakdowns by HQ state and NAICS code. Read them. A segment packed into one state is a different market from one spread evenly. A long tail of odd NAICS codes tells you the industry label is loose (Step 3).
Step 2: Filter on brackets, not exact numbers
Total headcount is also a blunt field. A 400-person company can be mostly engineers or mostly support, and the number does not say which. Pair the size bracket with a people filter such as jobFunction when the mix matters to your offer.
Exact headcount is the field teams over-trust. Use employeeRange for sizing, and use minEmployees and maxEmployees only when you need a cut inside a bracket.
The two filters do not match the same companies. minEmployees and maxEmployees return only records that hold an exact figure. A company with a bracket but no exact count is dropped.
Run both and compare:
curl -s -G "https://api.leadocean.io/v1/companies/count" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "industry=Software Development" \
--data-urlencode "hqCountry=US" \
--data-urlencode "employeeRange=51-200" | jq '.data.total'
curl -s -G "https://api.leadocean.io/v1/companies/count" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "industry=Software Development" \
--data-urlencode "hqCountry=US" \
--data-urlencode "minEmployees=51" \
--data-urlencode "maxEmployees=200" | jq '.data.total'The gap is the number of companies your exact-number filter silently hides. The same rule applies to minRevenue and minFounded: the filter only matches records where we hold the value.
Step 3: Check the industry label against its neighbours
Industry labels overlap. Resolve the enum before you filter, then decide which neighbours belong in your segment.
curl -s "https://api.leadocean.io/v1/enums/industry?q=software" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jq '.data.values'Filter values are exact strings. A wrong value returns an empty result, not an error (see Troubleshooting).
To go finer than a label, add keywords, which matches the company description, specialties and tags. Use excludeKeywords to drop false fits.
curl -s -X POST "https://api.leadocean.io/v1/companies/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"industry": ["Software Development"],
"hqCountry": ["US"],
"employeeRange": ["51-200"],
"keywords": ["payments"],
"limit": 1
}'count=true is a query parameter and the call spends no records. That is how you test a narrower definition of your segment before you pull a row.
Step 4: Sample-check a few records against your own data
Never validate a field by trusting it. Pull a small sample and compare it with what you know. Ten records is enough to see a pattern.
curl -s -X POST "https://api.leadocean.io/v1/companies/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"industry": ["Software Development"],
"hqCountry": ["US"],
"employeeRange": ["51-200"],
"limit": 10
}' > sample.jsonFor accounts already in your CRM, enrich by domain and diff the fields. A company that is not in the data returns a 404 and costs nothing.
curl -s -X POST "https://api.leadocean.io/v1/companies/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "acme.com"}' | jq '{name: .data.company_data.company_name, fetched_at: .data.meta.fetched_at}'Score the sample by field and write the result down.
| Field | Wrong in sample | What to do |
|---|---|---|
| HQ country | 0 of 10 | Safe to filter on |
| Headcount | 3 of 10 | Filter on the bracket only |
| Private revenue | Estimated | Use as a band, never a gate |
The numbers in that table are an example of how to log your own run, not a LeadOcean result. If headcount is wrong on 3 of 10 and HQ country on 0 of 10, trust country and treat headcount as a bracket. Private companies do not publish revenue, so treat that field as a rough band and never as a hard gate.
Two habits make this step worth the ten records. First, sample the same segment again after each refresh, so you see drift and not a one-off result. Second, keep the sample file with a date in its name. When a rep says a list was wrong, you can check what the data said on the day you pulled it.
Step 5: Re-enrich on a schedule
Records age. The dataset is refreshed monthly and each record carries its own fetched_at date. Use that date to decide what to refresh.
jq -r '.[] | select(.last_enriched < "2026-07-23") | .domain' crm_export.json |
while read d; do
curl -s -X POST "https://api.leadocean.io/v1/companies/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"domain\": \"$d\"}"
sleep 0.25
doneThe sleep keeps you under 5 requests a second on Free. On Agency the limit is 200 a second. For a full list refresh, POST /v1/exports takes up to 50,000 rows a request and 500,000 a day. The Exports page in the app (app.leadocean.io) shows the record price before you download. Bulk CSV export is not on Free.
What you get
On 2026-10-21 the free leadocean_count_companies tool returned these totals for US companies in Software Development.
| Filter | Companies |
|---|---|
employeeRange 51-200 | 9,261 |
minEmployees 51, maxEmployees 200 | 6,395 |
| Gap | 2,866 |
The NAICS breakdown on the bracket count shows how loose the label is. The top code, 541511, covers 5,890 of the 9,261. The rest spread over more than 15 other codes, down to single digits per code.
The free enum lookup for "software" on the same date returned seven industry values. They were Computer Software, Data Security Software Products, Desktop Computing Software Products, Embedded Software Products, IT System Custom Software Development, Mobile Computing Software Products and Software Development. Filtering on one label leaves the other six out.
A company enrich response has this shape (response shape from the API reference, placeholder values, fields abridged):
{
"success": true,
"data": {
"company_data": {
"company_name": "Acme",
"company_domain": "acme.com"
},
"company_metrics": {},
"meta": { "fetched_at": "2026-10-01", "schema": "leadocean.company.v1" }
},
"meta": { "credits": 1 }
}Run Step 1 again after every refresh. If the segment total moves by a large amount in a month, find out why before you launch a campaign on it. A big move usually means the label, a bracket or a country changed, not that the market did.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Send the key as x-api-key: $LEADOCEAN_API_KEY. Check it in the app under API keys. |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Wait for Retry-After, or add a sleep between calls. |
| 402 | Out of records on the Free plan | The 1,000 records are spent. Move to a paid plan, see pricing. |
| 200 with no rows | A wrong enum value, such as "Software" instead of "Software Development" | Resolve the value with GET /v1/enums/industry. Values are exact strings. |
| 404 on enrich | The company is not in the data | Nothing is charged. Try the linkedin_url instead of the domain. |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the pull across requests and days. |
FAQ
Which firmographic fields are least accurate?
Private-company revenue and funding stage. Private firms do not publish revenue, and funding changes with each round. Treat both as bands and check a sample before you filter on them.
How often should I refresh firmographic data?
Monthly is a sensible default for active pipeline, since the LeadOcean dataset itself is refreshed monthly. Refresh older records first, using each record's fetched_at date.
Why does my count change when I switch from a bracket to exact numbers?
Exact bounds match only records that hold an exact figure. Brackets also match records that hold only the band. Step 2 shows the gap on a real segment.
Should I fix bad fields in my CRM or filter them out?
Do both, in that order. Overwrite fields you have verified, such as HQ country. Filter out fields you cannot verify, such as revenue, so they stop deciding who gets a rep's time.
Does checking accuracy cost records?
Counts and enum lookups cost nothing, and an enrichment that finds nothing costs nothing. The sample in Step 4 spends one record per company returned.
Can I verify a field against a second source?
Yes, and for revenue you should. Pull the same domain from a second provider and compare. If the two disagree by a wide margin, drop the revenue gate from your scoring.
Related reading: best firmographic data providers, what firmographic data is, providers ranked for B2B sales teams, and the blog hub. Plans are on the pricing page.
Check your firmographic data against a free, sizeable segment
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →