Search for firmographic data and the top results list the same attributes, usually industry, company size, location, legal structure, and sometimes revenue. That list is accurate, and it describes what a company record can hold.
If you're about to build on a company data API, it's useful to know how often each of those fields is actually populated, and for which companies. A field list can't answer that, so we measured it.
We pulled 240 live company records from Enrich Layer's Company Search endpoint on September 22, 2026, taking 40 from each of six employee-count bands, from companies of 1 to 10 people up to 5,001 and above. Stratifying by size matters because a random draw is dominated by very small companies, and fill rates change with company size. We counted a field as populated only when it held a real value. A null, an empty list, or an object full of empty lists all counted as missing.
We also pulled 48 of the same companies through the Company Profile endpoint with every optional field turned on. Unless noted, everything below comes from those two samples. Your segment will differ, and the last section shows how to check it yourself.
What the record reliably contains
Here's every field that came back populated at least some of the time, across all 240 records.
| Field | Populated |
|---|---|
linkedin_internal_id | 99.2% |
universal_name_id | 99.2% |
name | 99.2% |
search_id | 99.2% |
hq | 99.2% |
company_size_on_linkedin | 99.2% |
follower_count | 97.1% |
industry | 93.8% |
locations | 93.3% |
company_size | 83.8% |
website | 80.8% |
description | 75.8% |
profile_pic_url | 72.9% |
background_cover_image_url | 70.0% |
updates | 61.7% |
company_type | 54.2% |
tagline | 51.2% |
founded_year | 49.6% |
specialities | 38.3% |
similar_companies | 23.3% |
affiliated_companies | 9.6% |
Five more fields are off by default and don't appear here. They're covered further down.
The top six fields are present on 238 of 240 records. They're what you build keys and joins on. Of the identifiers, linkedin_internal_id is the one to store, because the API documents it as immutable. Our guide to identity resolution covers why a stable join key matters when you match records across systems.
The next tier does most of the segmentation work. industry, locations, and the headcount band company_size all land between 83.8% and 93.8%. website is the one to watch. One record in five came back without it, and domain is a common key for matching companies to CRM records, so a fifth of these companies can't be matched to your existing accounts that way.
Two employee counts that disagree most of the time
Every record answers the size question two times. company_size is a range, and company_size_on_linkedin is a single number. Here's how they look on one record from the sample:
{
"company_size": [11, 50],
"company_size_on_linkedin": 4
}
That record isn't unusual. Of the 201 records carrying both fields, 120 (59.7%) had a headcount number that fell outside their own range.
The part that affects your code is which field the search filter reads. On Company Search, employee_count_min and employee_count_max filter on company_size_on_linkedin. In our sample, 236 of 238 records returned for a size filter were in range by the number. Checked against company_size, between a third and three-fifths of each result set appeared out of range, about half overall.
So if you filter for 51 to 200 employees and then spot-check the results against the range field, more than half of them will look wrong, because you're reading a different field from the one you filtered on. Decide which measure of size you mean, filter on it, and read the same field back. If you're building an account list from these filters, this walkthrough covers the rest of the process, including why a country filter matches any office rather than headquarters.
The record thins out with company size
Averaged across all 240 records, the lower half of the table above looks like a gradual slope. Split by size, a sharper pattern appears.
| Field | 1–10 | 11–50 | 51–200 | 201–1,000 | 1,001–5,000 | 5,001+ |
|---|---|---|---|---|---|---|
industry | 85% | 90% | 95% | 95% | 98% | 100% |
website | 60% | 82% | 90% | 70% | 92% | 90% |
description | 42% | 80% | 68% | 80% | 90% | 95% |
founded_year | 28% | 60% | 52% | 55% | 60% | 42% |
company_type | 20% | 62% | 50% | 50% | 62% | 80% |
specialities | 22% | 32% | 38% | 28% | 65% | 45% |
With 40 records per band, individual cells are noisy. The dip in website at 201 to 1,000 employees is more likely sampling than a real effect. What holds on every row is the step at the bottom. Companies with 10 or fewer people have the thinnest records on every field in the table.
If your market is very small companies, plan for records where you usually know the industry and the size, but are missing the website four times in ten and the founding year seven times in ten. The downstream effect is easy to miss. A lead-scoring model that weights founded_year or company_type can score small companies lower because of missing data, and nothing about the company itself will have caused it.
How old the records are
meta.last_updated gives the date each record was last refreshed. It was present on 238 of the 240, so age is something you can measure directly.
| Age of record | Share |
|---|---|
| 0–7 days | 59.7% |
| 8–29 days | 9.7% |
| 30–89 days | 13.9% |
| 90–365 days | 12.6% |
| Over a year | 4.2% |
The median record was 6 days old, and the mean was 54, because the distribution has two parts. Most records were refreshed within the past week, and a minority were months old, with the oldest at 399 days. 69% were 29 days old or less.
Only 6 of the 238 records were within ten days of that 54-day average, so a single freshness number tells you little about the records you'll actually receive. If a vendor quotes one, ask for the distribution instead.
Because every record carries its own timestamp, you can also check age yourself. Decide how old a record your use case can tolerate, then filter on meta.last_updated when you read the data. A quarterly plan can work with records a few months old, while a workflow that reacts to recent changes at a company needs records refreshed within days.
Five fields stay off until you ask for them
By default, the Company Profile endpoint leaves out categories, funding_data, exit_data, acquisitions, and extra. Each has its own request parameter, which defaults to exclude. None of them were populated on any of the 240 search records, so a coverage audit that doesn't know they exist will score them at zero.
With all five set to include, on the 48 profile records:
| Field | Populated |
|---|---|
categories | 97.9% |
extra | 93.8% |
funding_data | 4.2% |
exit_data | 0% |
acquisitions | 0% (see the next section) |
extra holds a handful of company-level attributes, and the one present most often was ipo_status, on 44 of 48 records.
Funding and exit fields are populated only for companies that have had those events, so most small companies come back empty.
One field also changes with the endpoint you call. For the same 48 companies, similar_companies was populated on 14 through Company Search and on 42 through the Company Profile endpoint. affiliated_companies moved less, from 5 to 8, and the descriptive fields we compared were populated at the same rates on both. If related companies matter to your workflow, fetch them from the profile endpoint.
A field can be present and still be empty
acquisitions appeared on all 48 profile records with the options turned on, but returned data on none of them. Every one returned the same structure:
{
"acquisitions": {
"acquired": [],
"acquired_by": null
}
}
A coverage check that asks whether the field exists scores this at 100%. The real figure for this sample is 0%. Any fill rate built on presence checks overstates coverage this way, whoever calculates it. The way around this is to look inside the value and count a field only when at least one non-empty value sits somewhere within it.
The zero mostly reflects the sample. These were largely small companies that hadn't acquired anyone. We also checked eight large companies known to have made acquisitions, and the field was populated on those.
Where firmographic data comes from
Most firmographic data starts with what a company publishes about itself, such as website, its profiles on public platforms, press coverage, and, for some companies, government filings. Providers collect that material, normalize it into fields, and fill gaps from other firmographic data sources. Enrich Layer uses public data and third-party vendors.
That origin is the most likely explanation for the company-size pattern above. A company with five people publishes far less about itself than one with five thousand, so a record built from public information has less to draw on. The gap between the smallest companies and the rest was widest for company_type and description.
Fields within one record can also differ in how they were produced. A website is usually recorded as the company published it, an industry label is typically mapped onto a fixed list of categories, and some fields in some datasets are inferred rather than observed. Our guide to evaluating B2B data vendors covers how to tell observed, normalized, and inferred fields apart, and why inferred fields carry the most risk.
Some provenance is visible on the record itself. meta.last_updated shows when it was last refreshed, and the API documents two of the opt-in fields, extra and acquisitions, as coming from external sources beyond the base profile.
What a firmographic record leaves out
Naming what's missing is part of evaluating any dataset. A record with 28 keys can look complete and still lack the one attribute your model needs. These records don't include:
- Revenue. It appears in most definitions of firmographic data, and there's no revenue field in these records.
- Buying intent. Nothing in the company record indicates whether a company is in market for anything.
- Reporting lines. People at a company are available by title through separate employee endpoints, but the company record doesn't describe structure.
- The software a company runs. Technology usage isn't part of the record.
- Headcount history. The record holds only today's headcount.
- Government industry codes. SIC and NAICS codes aren't part of the record. Our post on reading product and service companies from tax filing codes covers what those codes are good for.
For how firmographic data relates to the other kinds of company data, including technographic, funding, and intent data, see business data explained.
Run the same check on your own segment
The numbers above describe one stratified sample taken on one day. Your fill rates depend on which companies you care about, and the figure that matters for you is the one for your segment. The check itself is short:
def has_value(v):
"""True if v holds at least one non-empty value anywhere inside it."""
if v is None:
return False
if isinstance(v, (list, tuple)):
return any(has_value(x) for x in v)
if isinstance(v, dict):
return any(has_value(x) for x in v.values())
if isinstance(v, str):
return v.strip() != ""
return True # numbers and booleans, including 0 and False, are values
def fill_rates(records):
"""Share of records in which each field holds a real value."""
fields = {key for record in records for key in record}
n = len(records)
return {
field: sum(has_value(record.get(field)) for record in records) / n
for field in sorted(fields)
}
To use it:
- Pull records for a set of companies that looks like your market, split the way your market splits, by size, region, or both.
- Run
fill_ratesover them and sort the result. - Compare the result against the fields your scoring, routing, or reporting actually reads.
For a fuller method, including how to build a test set with known answers, see how to test and evaluate a data enrichment dataset. Then run the same check against every vendor you're evaluating, including us.
