Work Email to Firmographic Profile Mapping

Filtering and resolving work emails to build accurate company records without wasting API calls.

· 9 min read
Cover illustration for “Work Email to Firmographic Profile Mapping”
Autofill & Enrichment · September 25, 2026 · 9 min read · 2,044 words

A work email looks like almost nothing: a username, an @ sign, a domain. That string is the seed for an entire company profile, complete with headcount, industry code, funding stage, and the name and title of whoever typed it. Getting from one to the other takes seven distinct steps, and most of the trouble developers run into traces back to skipping one of them.

The step people skip most, and the one that costs the most when skipped, is checking whether a domain is worth resolving before an API call goes out on it.

Break the string down and two parts carry any signal at all: the local part before the @, and the domain after it. They are not equally useful, and treating them as if they were is the first mistake.

The domain does the real work. In nearly every B2B case it maps one-to-one to a legal entity, which makes it the primary key for everything downstream. It's also the cheap signal to resolve: one domain lookup enriches every employee at that company at once, rather than running the same check person by person, address by address.

The local part matters less, but not nothing. A naming convention like first.last or f.last says something about company size and IT policy. Larger, older organizations standardize their email formats; a five-person startup just uses first names and calls it done. Whether the domain is one the company owns outright, versus some shared or generic hosting setup, hints at how established the business actually is.

Then there's the domain that should stop the pipeline cold: gmail.com, outlook.com, yahoo.com, any free consumer provider. These don't map to a company, so running one through a firmographic lookup burns an API call on a record that will never resolve into anything. Check for this before spending a cent, not after. Skipping that check does not make the cost disappear, it just moves downstream, buried in a bill nobody wants to itemize.

Domain-to-entity resolution: turning a string into a named company record

Once a domain clears that filter, the job is attaching it to an actual company record: company name, headquarters country, employee headcount band, industry classification code (SIC or NAICS), annual revenue range, founding year, funding stage.

Domain-level data holds up better over time than contact-level data does, and that gap is the reason domain resolution has to happen first and separately. A company's headcount band or industry code tends to be more stable than contact-level fields. A person's job title or direct line turns over far more frequently. Mix the two kinds of lookup together and the fast-decaying one drags down the reliability of the slow-decaying one.

The main approaches to getting from domain to record are not equally good. A static vendor database matches the domain against a pre-built index and returns an answer fast, but that answer is only as current as the last time someone refreshed the index, which could be months back. Live web retrieval crawls the company's own site, its LinkedIn page, public registries, at the moment of the query. Slower, yes. But it reflects the company as it stands today, not as it stood on whatever date the vendor's crawler last passed through.

This stage, at minimum, needs to produce four fields: legal name, primary industry, headcount band, HQ location. Those four are often enough on their own to tell a sales team whether a lead fits the ideal customer profile or should get passed on without another look.

Identity graph matching: connecting domain to a specific person

Domain resolution answers what company this is. Identity graph matching answers who, specifically, sent the email. The method: take the address, check it against public professional signals, LinkedIn profiles, published bylines, conference speaker lists, GitHub commit history, press mentions, and see what lines up.

What comes out is a set of person-level fields: name, job title, seniority band, tenure at the company, sometimes a direct LinkedIn URL. This is a genuinely different layer from the firmographic work above it, and the two need to stay separate in the pipeline, because they decay at different rates and draw from different sources. Bundle them into one lookup and both get harder to maintain, not easier.

Not every signal deserves equal trust, and treating them as if they did is how bad matches slip through looking just as solid as good ones. A confirmed LinkedIn profile beats a professional directory listing. A directory listing beats a byline on an article. A byline beats guessing a name off an email naming convention alone. Confidence scores on person-level fields should reflect that ordering directly, not average it away.

Static vendor databases and firmographic profile accuracy over time

The case against static databases comes down to decay rate, and the decay rate is brutal. B2B contact data decays somewhere in the 25 to 30% range annually. Running that against a 10,000-record database that nobody touches costs roughly 2,500 to 3,000 usable contacts a year.

Break it down by field and the picture sharpens. About 25% of contacts see a title change in a given year. Roughly 12% change companies, which severs the domain-to-person link. Bounced emails alone pile up at close to 22% annual decay, a rate no quarterly refresh can absorb. None of this is edge-case noise. It's the ordinary churn of people changing jobs and companies restructuring, moving faster than any quarterly refresh cycle can track.

Measured across Cleanlist's customer base in Q4 of 2025, contact data decayed at roughly 2.1% on a rolling monthly basis. That sounds small until it compounds: a database refreshed once a quarter is already meaningfully stale by the time anyone actually queries it.

Poor data quality costs the average organization $12.9 million a year. It accumulates through disconnected phone numbers, bounced emails, and missed title changes that nobody caught in time.

How live web extraction replaces the stale-database dependency

The fix is to flip the model. Instead of pre-fetching data and hoping the record sitting in an index still holds up, live web enrichment fetches the source at the exact moment someone queries it. A fresh fetch beats a cached guess, every time, regardless of whether the cache is a week old or a quarter old.

Operationally, live means the crawler hits the company's own website, its LinkedIn company page, its press room, its open job listings, and any funding announcements right now, not whenever a vendor last ran its crawl. These are primary sources, and primary sources are always going to be more current than an aggregated index sitting behind them. An index is, by definition, a snapshot of something that has already moved on.

Job listings deserve particular attention here, because they're an unusually rich firmographic signal that most static databases miss. A company's open roles say a lot on their own: headcount trajectory, which departments are actively growing, what's in the tech stack, where the company is expanding geographically. Usage of job listings as a discovery signal in AI-driven pipelines reportedly surged more than 50 times over in 2025, which says something about how much value sat unused in that data for years.

None of this works if the crawler can't actually read the page, though. Most modern company sites are JavaScript-heavy, so a plain HTTP GET request returns an empty shell of HTML with nothing rendered. Getting usable text means rendering the page the way a browser would. Fetching raw markup alone won't cut it.

Structuring the extracted content into a firmographic schema

Raw crawled text isn't useful to an application by itself. It needs a schema. A developer declares the fields wanted, company_name, headcount_band, hq_country, industry_code, funding_stage, tech_stack, and the extraction layer fills them in from whatever it just crawled. That schema is the contract between what gets pulled off the web and what the application actually consumes.

A few decisions in that schema design determine how usable the resulting data is downstream, and getting them wrong turns a clean dataset into one that quietly rots. Categorical fields, headcount bands, industry codes, need a controlled vocabulary rather than free text. Free text turns "51-200 employees" into a dozen slightly different label variants across records, and nothing joins cleanly after that. Every field should carry a confidence score alongside its value, and a missing value should stay null rather than get filled in with a guess. A blank field is honest. A hallucinated one looks correct right up until someone acts on it, and by then the damage is already done.

Reconciling across sources adds a further layer. Headcount might show up on the company website, on LinkedIn, and in a funding announcement, and those three numbers frequently won't agree with each other. The pipeline needs an explicit rule for that conflict, recency-wins, trusting whichever source published most recently, or authority-wins, trusting the company's own site over a third party. Pick one before the records pile up, not after.

Single-source match rates typically run somewhere between 65 and 82%, depending on the provider and geography. Layering sources helps, but with real diminishing returns: right-person match rates climb from around 51% with a single provider to roughly 65% once four sources stack in a waterfall, and each additional source adds less than the one before it. Schema design should expect partial fills as the norm and build the downstream logic around that.

Putting the full pipeline together: from raw email to enriched firmographic record

Diagram: Seven Steps from Raw Email to Enriched Firmographic Record. Visualizes: Visualize the seven-step pipeline that transforms a raw email address into an enriched company record.

Laid out end to end, the pipeline runs in seven steps.

Step 1, parse and validate. Extract the domain, check it against a blocklist of free and consumer mail providers, resolve the MX record to see what mail platform the company runs on.

Step 2, domain-to-entity resolution. Map the domain to a company name, HQ country, headcount band, and industry code. This is the firmographic foundation everything else builds on, and getting it wrong here poisons every step after it.

Step 3, live web retrieval. Crawl the company website, its LinkedIn company page, its press room, and its job listings at query time. Render any JavaScript. Strip out navigation bars, footers, and other boilerplate that adds noise instead of signal.

Step 4, schema-driven extraction. Pass the cleaned content through structured extraction against the schema defined earlier. Handle missing fields explicitly as nulls, never as guesses.

Step 5, identity graph matching. Resolve the email address to a person: name, title, seniority band, a LinkedIn URL where one can be confirmed.

Step 6, multi-source reconciliation. Merge fields pulled from different sources using a recency-wins or authority-wins rule, and attach a confidence score and timestamp to each field individually, not to the record as a whole.

Step 7, continuous refresh. Schedule re-enrichment at the field level on a cadence that matches the actual decay rate, close to that 2.1% monthly figure, instead of treating enrichment as a one-time job.

Three failure points show up over and over, and they're avoidable. Catch-all domains trip up step 1 regularly: a domain that accepts mail for any username at all needs manual review, not a pass-through as a verified address. JavaScript-heavy pages break step 3 whenever the crawler only fires a plain HTTP GET and gets back shell HTML with nothing real inside it. Headcount pulled from three different sources creates conflicts at step 6, so the merge rule has to be decided in advance, never improvised per record on the fly.

The freshness argument is the whole case in one sentence: a pipeline that fetches live at the moment of query produces a record that reflects the company as it exists today, while a vendor's index reflects only the last time it happened to get refreshed. That gap, between what a stale database says and what's actually true right now, is where the $12.9 million in annual cost from bad data quietly piles up, one disconnected phone number and one outdated title at a time.

None of this is simple to build. Domain parsing, identity matching, schema reconciliation, each layer carries real engineering weight on its own. But every step is well-defined enough to reason about independently, and the hardest layer to build and keep current, live extraction itself, is also the one most directly suited to a purpose-built API rather than something maintained in-house from scratch.

More in Autofill & Enrichment