A website that does business publishes a way to contact it. That is not an accident of the web; it is the point of having a website. Which means that for any company you want to reach, there is almost certainly a real, working, published address somewhere on their domain.
Finding it is the easy part. What is hard — and what almost every extraction workflow gets wrong — is finding it accurately: separating the address that reaches a human from the image-tagged noise, the tracking pixels, the example addresses in the privacy policy, and the noreply@ that goes nowhere.
This piece covers where the addresses actually live, why the naive approach produces junk, what the law says, and how to build a workflow that yields a list worth sending to.
Start with the law, because this one has real teeth
Extracting published contact details from a company website sits in a much better legal position than scraping a social network, but "better" is not "unregulated", and the distinctions matter.
Robots.txt. A site's robots exclusion file tells automated agents which paths they may not fetch. It is not a law, and ignoring it is not a crime — but it is a clear, published statement of the site owner's wishes, and disregarding it is exactly the kind of fact that makes a legal position worse rather than better. Honour it.
Rate limits. Hammering a small company's server with hundreds of requests a second is, at minimum, rude, and depending on the effect and the jurisdiction can constitute unauthorised interference with a computer system. Crawl slowly. One request per second per domain is plenty and will never get you in trouble.
GDPR. An address like info@company.com is a corporate address and is not personal data. An address like sarah.chen@company.com identifies a person and is personal data, even though it is published on a corporate site and even though it is a work address. Collecting it requires a lawful basis; using it for outreach requires that your legitimate interest survives a balancing test against her reasonable expectations. That is a real test, and "it was on the internet" does not pass it by itself.
The practical upshot: extracting a company's published contact addresses to reach the company is on solid ground. Building a database of named individuals' addresses to spray with a template is not, and the fact that the data was public does not change that.
Where the addresses actually are
Naive extractors fetch the homepage, run a regex, find nothing, and report "no emails found". That is usually wrong. The homepage is almost the last place a company puts its address.
In rough order of yield:
- The contact page. The obvious one, and the highest-value. Our contact page finder exists because locating it is itself a small problem — it might be
/contact,/contact-us,/get-in-touch,/about/contact, or a footer link with no URL convention at all. - The footer. Present on every page, frequently carries the general enquiries address, and is often the only place it appears.
- The team or about page. Where named individuals appear, along with their addresses. Higher value and higher legal sensitivity — see above.
- The privacy policy and terms. Almost always contains a real, monitored address, because a DPO or legal contact is a regulatory requirement. This is the single most reliable page on a website for finding an address that a human actually reads, and almost nobody looks there.
- Job listings. Frequently include a hiring manager's address. Our job listing extractor targets these.
- Press and media pages. Usually a real person, usually monitored, usually responsive.
- PDF documents. Whitepapers, brochures, annual reports. Rarely crawled, often full of contact details. Our PDF email extractor handles these.
The privacy-policy trick is worth dwelling on. A company can hide its sales contact behind a form, but it cannot hide its data-protection contact — that address has to be real and it has to be published, because the law requires it. It is not the right address for a sales pitch, but it is proof of the domain's email pattern, and from a pattern you can derive everything else.
Why a regex alone gives you garbage
The instinct is to write a pattern that matches something@something.something and run it over the HTML. It will match. It will also match a great deal of material you do not want:
- Example addresses.
john.doe@example.com,you@yourcompany.comin a form placeholder,name@email.comin the newsletter signup. All syntactically perfect, all worthless. - Image and asset filenames. Certain naming conventions produce strings that a loose regex will happily read as an address.
- Tracking and analytics identifiers. Embedded in scripts, often shaped like addresses.
- Sentry, Intercom and other vendor keys. These frequently contain
@and dots in a pattern that fools naive matching. - Addresses in the schema.org markup — usually good, actually, but they need parsing rather than regexing.
- Obfuscated addresses.
sarah [at] company [dot] com, or an address assembled by JavaScript at runtime specifically so that regex scrapers miss it. A plain fetch will not see these at all.
So the extraction step is not "find things that look like emails". It is: find things that look like emails, then filter out the ones that cannot be real, then classify what remains. That classification is most of the work and almost none of the tooling.
The filter list that turns junk into a usable list
What to discard, roughly in order of how much damage it does if you keep it:
- Placeholder domains.
example.com,example.org,domain.com,yourcompany.com,email.com,test.com. These are reserved or conventional and will never be real. - Addresses whose domain does not match the site you are crawling. Usually a customer testimonial, a partner logo, or a vendor's support address. Not useless, but not what you were looking for, and mixing them in silently corrupts the list.
- Image file extensions. If it ends in
.pngor.jpg, it is a filename that fooled your pattern. - noreply, no-reply, donotreply. Real addresses. Nobody reads them. Sending to one is a guaranteed non-answer, and in some setups an automatic complaint.
- Role addresses, depending on your purpose.
info@,sales@,support@are real and monitored, but they are read by a rotating cast and they receive enormous volumes of unsolicited mail. Useful for establishing the domain pattern; poor targets for cold outreach.
Our business-vs-free email detector handles a related distinction — separating sarah@company.com from sarah@gmail.com — which matters more than it sounds, because a personal free-mail address on a corporate site usually means a very small business, and that changes both your targeting and your message.
JavaScript, and the addresses you cannot see
A meaningful and growing share of sites do not put the address in the HTML at all. It is assembled by JavaScript at runtime, precisely so that a naive fetch-and-regex finds nothing.
This is an entirely reasonable thing for a site owner to do, and it is worth reading as the signal it is: they do not want automated collection. That is a preference worth respecting, and it is also a practical obstacle — a simple HTTP GET will return HTML with a gap where the address should be.
Rendering the page in a real browser engine defeats this, and there is a genuine question about whether you should. Our position is that if a site has gone to the trouble of hiding an address from automated collection, the polite and defensible response is to use the contact form they provided instead of engineering around their wishes. It is also usually the more effective response: a form submission reaches a human, and a scraped address that they deliberately obscured is likely to be treated as spam on arrival.
Phones, socials, and the rest of the contact graph
An email address in isolation is a thin thing. The same crawl that finds the address will, at no extra cost, find a good deal more:
- Phone numbers — our phone number extractor pulls these, and they are the highest-intent channel available for a small business.
- Social profiles — the social link extractor finds them, and they are the fastest route to knowing whether a company is active or dormant.
- The technology stack — visible in the page source, and a strong qualifier. A company running a competitor's product is a different prospect from one running nothing at all. Our tech stack detector reads this.
- The company's own metadata — schema.org markup often contains the legal entity name, address, and founding date, all cleanly structured and all ignored by most extractors.
The Govarova Contact Extractor pulls all of these together in a single pass, which matters less for convenience than for coherence: an email address that arrives alongside the phone number, the stack, and the social presence is a qualified lead. The same address on its own is a row in a spreadsheet.
Verify what you extracted, because extraction is not validation
An address that appears on a website is not necessarily an address that works. Sites go stale. The sarah@ on the team page left in 2023 and nobody updated the page — team pages are famously the last thing anyone maintains.
So extraction gives you candidates, and candidates get verified. This is the same discipline as everywhere else in this business, and skipping it produces the same result: a bounce rate that damages delivery to the addresses that are real. The email verifier checks a single address; the verification API does it at volume; and the reasoning behind why this matters so much is set out in our piece on bounce rates and sender reputation.
Scaling it without becoming a nuisance
One site is a curiosity. Ten thousand sites is a pipeline, and it is at that scale that the difference between a careful extractor and a reckless one becomes visible from the outside.
The rules that keep you welcome:
- Identify yourself in the user agent. A crawler with a real name and a URL explaining what it does is a crawler that gets a warning email rather than a block. An anonymous one gets blocked.
- Respect robots.txt, every time, without exception.
- Rate limit per domain, not globally. Ten thousand requests a second across ten thousand domains is one request per domain per second, which is fine. Ten thousand requests to one domain is an attack.
- Cache aggressively. If you crawled a site last week, do not crawl it again this week. Most contact pages change once a year.
- Back off on errors. A 429 or a 503 means stop, not retry immediately.
None of this is technically difficult. It is skipped because it is boring and because the consequences fall on someone else — which is precisely why the people who do skip it end up on blocklists, and why their extraction pipeline stops working about six months in.
Local businesses: a different problem entirely
Everything above assumes a company with a real website and a marketing team. A large share of B2B prospecting is not that. It is plumbers, dentists, restaurants, independent retailers — businesses with a one-page site, a Facebook page, or nothing but a Google Business Profile.
The extraction problem inverts. There is no team page and no privacy policy. What there is:
- The Google Business Profile, which carries the phone number, the hours, the category, and frequently a website link that goes to a Facebook page.
- A free-mail address. The owner's
@gmail.com, used as the business address. This is not a mistake to be corrected; it is the actual contact route, and treating it as low-quality data will cause you to discard your best local prospects. - Review activity, which is the single best indicator of whether the business is alive and whether anyone is paying attention to it.
For this segment, the business-vs-free email detector is doing something more useful than it first appears: a free-mail address on a corporate domain-holding company is a red flag, but a free-mail address for a two-person plumbing firm is simply how that business works. The same signal means opposite things depending on the segment, which is precisely the sort of nuance a scoring model gets wrong and a human gets right in a second.
What "no emails found" usually means
When an extractor reports nothing, it is almost never because the company has no email address. It is one of five things, and each has a different fix:
- You only fetched the homepage. The address is on
/contact. Fetch more pages. - The address is rendered by JavaScript. A plain HTTP fetch sees a gap. As discussed above, this is a deliberate signal from the site owner, and the right response is usually the contact form.
- The address is an image. Some sites still render the address as a PNG specifically to defeat text extraction. Reading it requires OCR, and the fact that they went to that length is, again, a message.
- It is behind a form and genuinely nowhere else. Increasingly common, and a legitimate choice on their part.
- Your filter was too aggressive. You found
hello@company.com, decided it was a role address, and dropped it — leaving you with nothing. For a ten-person company,hello@is the founder's inbox.
That last one is the most common and the least suspected, because the pipeline reports success at every stage and produces an empty result.
Deduplication, and the mess it hides
Crawl a thousand sites and you will not get a thousand clean rows. You will get seven thousand candidate addresses, most of which are the same handful repeated across every page of each site, plus a long tail of vendor and partner addresses that belong to entirely different companies.
Deduplication is not just "remove exact duplicates". The cases that matter:
- Case.
Sarah@Company.comandsarah@company.comare the same mailbox. The local part is technically case-sensitive per the RFC; in practice no real mail system treats it that way. Lowercase everything. - Gmail dots and plus-addressing.
s.chen+signup@gmail.comandschen@gmail.comare the same inbox. This matters enormously for signup abuse and not at all for B2B, so know which problem you are solving. - The same address on two domains. Usually a parent company or an agency. Keep both rows, but know they are one human, or you will email the same person twice from two campaigns and look exactly like the spammer you are trying not to be.
- Subdomains.
info@company.comandinfo@mail.company.commay or may not be the same. Check the MX.
Our CRM data formatter handles the normalisation, which is dull, mechanical, and the difference between a list you can import and a list that corrupts your CRM.
How to know whether the list you built is any good
Before you send anything, four checks will tell you whether the extraction worked. They take minutes and they save campaigns.
- What fraction of rows have an address at the same domain as the site? If it is below about 70%, your filtering is letting foreign addresses through and the list is contaminated with vendors and testimonials.
- What fraction are role addresses? A list that is 90%
info@is a list of front doors, not of people. That may be fine for some purposes and is useless for personalised outreach. - What is the verification pass rate? Run a sample of a hundred through the verifier. If more than a tenth fail, something upstream is broken — you are extracting stale pages, or deriving without checking.
- Spot-check twenty by hand. Actually open the source pages. This is the check nobody does and it catches the errors that no metric will — the addresses lifted from a customer testimonial, the ones that belong to the site's web design agency, the ones that were in an example in the docs.
A list that passes these four is worth sending to. A list that has not been checked is a hypothesis about a list, and the first campaign is where you find out.
The point of all this
Extraction is a means, and it is worth being clear about the end, because the tooling makes it very easy to lose sight of it.
The goal is not a large list. A large list is trivially obtainable and almost worthless. The goal is a list where every row is a company you deliberately chose, a contact route that actually reaches a human, and enough context that you have something true and particular to say when you write.
Extraction gets you the contact route. Enrichment gets you the context. Choosing the companies is your job, and it is the part no tool can do, because it depends on knowing what you sell and who it is genuinely good for — which is a question about your business, not about theirs.
Everything else in this article is plumbing in service of that. Get the plumbing right and it becomes invisible, which is exactly what plumbing should be. Get it wrong and you will spend a quarter wondering why a list of ten thousand addresses produced four replies and a damaged sending domain.
The workflow, end to end
- Start from a company list, not a URL list. Who do you actually want to reach? Firmographics first.
- Find the contact page, the footer, and the privacy policy. Three fetches, not a full-site crawl.
- Extract candidates — emails, phones, socials — from those pages.
- Filter hard. Drop placeholders, foreign domains, noreply, and image artefacts. Expect to discard more than you keep.
- Derive the pattern from any real address you found, which then gives you access to named individuals without ever having scraped a social network. This is the step that makes the whole thing work, and it is covered in our piece on finding an address from a LinkedIn profile.
- Verify everything before it goes anywhere near a campaign.
- Enrich what survived, so you know who you are writing to and why. Enrichment is what turns an address into a reason to write.
Notice that extraction is step three of seven, and it is the step everyone thinks is the whole job. The filtering, the verification, and the enrichment are where a usable list actually comes from — and where almost every tool on the market quietly leaves you to your own devices.
Doing this across hundreds of sites is exactly what Govarova’s Contact Extractor is for. See it on your own list with a free trial.