Key takeaways
Scraping publicly listed business emails for B2B outreach is usually permitted under GDPR (legitimate interest) and CAN-SPAM, as long as you identify yourself and offer opt-out.
There are five practical methods, ranging from a Cmd+F scan to a Python script. The right one depends on the volume and how technical you are.
Most companies do not list their decision makers on the contact page. Scraping caps out fast, which is why pattern detection plus verification beats it for sales prospecting.
Catch-all domains, obfuscation, and honeypots are the three reasons scraped lists bounce. Verification before sending is non-negotiable.
For hundreds or thousands of domains, an API plus a workflow tool like n8n or Make replaces the entire pipeline with about 20 minutes of setup.
TL;DR
You can scrape a single page in 10 seconds with a regex pasted into Chrome DevTools. You can scrape an entire site in five minutes with a 30-line Python script. You can scrape 500 sites in an afternoon with a SaaS API and a workflow builder. The technique is the easy part.
The hard part is what happens after. Scraped lists are noisy. They include outdated addresses, role accounts (info@, sales@), catch-all responses that look valid but bounce, and the occasional honeypot planted to flag spammers. Every address you collect has to be verified before it sees a campaign.
And scraping has a ceiling. Most decision makers do not publish their email on the company website. If you need to reach a Head of Growth, a VP of Engineering, or a buyer at a 200-person company, scraping the contact page returns info@ at best. That is when you stop scraping and start using a domain-to-pattern finder. We will cover both paths here.
Is scraping emails from a website legal?
Short answer: yes for business emails used in a professional context, with caveats. Long answer below, broken down by the three regulations that actually apply to B2B prospecting.
GDPR (Europe)
GDPR treats personal email addresses as personal data, but it allows processing without consent under "legitimate interest" if three tests are met: the interest is legitimate (selling to a business), the processing is necessary (the email is the only way to start the conversation), and the prospect's rights are not overridden (the message is relevant, identifies the sender, and offers opt-out).
Business addresses (john@acme.com) tied to a person doing a job are fair game for B2B outreach when this balancing test passes. Generic role accounts (sales@acme.com) carry even less personal-data weight. Personal addresses (john@gmail.com) are a different conversation and should be avoided.
CAN-SPAM (United States)
CAN-SPAM does not require consent before sending. It requires that the message identify itself as commercial (not necessarily in the subject line, but in the body), include a physical address, and offer a one-click way to opt out. Scraping addresses is not prohibited. Sending with a deceptive header or a missing unsubscribe link is.
Practical version: a US-targeted cold email with a clear sender, a real physical address in the footer, and a working unsubscribe link is compliant.
CCPA (California)
CCPA gives California residents the right to know what data you have on them and to request deletion. For B2B prospecting, the practical impact is: keep a record of where each address came from, honour deletion requests promptly, and avoid selling the data to third parties unless you have disclosed it.
What is clearly off the table
- closeScraping personal email addresses without consent.
- closeBypassing a login, paywall, or CAPTCHA to reach the addresses.
- closeIgnoring robots.txt where the site explicitly disallows crawling.
- closeSelling the list, or sending to it after a recipient has opted out.
This is a pragmatic guide written by an operator, not a lawyer. If you are running a large outbound operation in a regulated industry, get qualified legal advice for your jurisdiction.
5 methods to scrape emails from a website
Ranked from fastest-to-set-up to most-scalable. Pick based on your volume and your tolerance for writing code.
Manual: View Source or Cmd+F
Best for: 1 to 5 emails, one-off lookups.
Open the contact, about, or team page. Press Cmd+F (Ctrl+F on Windows) and search for "@". Most contact pages will surface the addresses immediately. For addresses tucked into the page source but not visually rendered, right-click and choose View Page Source, then search for "mailto:" or "@".
This works on roughly 60 percent of small business websites and almost no enterprise sites. The ceiling is hit fast, but for one-off research it is a zero-tool, zero-friction option.
Chrome extensions
Best for: 10 to 100 emails across a few sessions.
A handful of Chrome extensions walk the DOM, extract every email address on a page, and dump them into a CSV. Hunter, Apollo, Lusha, and Snov.io all ship one. They are convenient for browsing-and-scraping workflows where you are already on a prospect's site reading.
Trade-off: extensions pull addresses already visible on the page. They do not infer the head of marketing's email from the careers page. They also draw from the vendor's database, so what you get is a hybrid of "scraped from this page" and "matched from our database". Read the extension's behaviour carefully before relying on it.
Browser DevTools regex
Best for: 1 site, every address on every page, in 10 seconds.
The fastest method for a single page. Open Chrome DevTools (Cmd+Option+I), switch to the Console tab, and paste:
You will get a deduplicated array of every address that appears anywhere in the rendered HTML, including JSON-LD blocks, hidden mailto links, and inline scripts. View Source misses the JavaScript-injected ones. DevTools does not.
For sites that load content dynamically (single-page apps), wait until the page settles before running the snippet. For obfuscated addresses (more on that below), this regex catches the cleartext ones and misses the rest.
Python script with BeautifulSoup
Best for: 1 to 50 sites, repeatable, scriptable.
The classic scraping stack. 30 lines of Python, runs on a laptop, handles multiple pages per domain (contact, about, team), and writes results to a CSV. Here is a working starter:
What this gets you: every public-facing address listed on the contact, about, and team pages of each domain. What it does not get you: JavaScript-heavy sites that render emails client-side (use Playwright for those), obfuscated addresses, and decision makers who do not list themselves publicly.
Add rate limiting (a one-second sleep between requests) and respect robots.txt. Scraping responsibly keeps your IP off blocklists.
SaaS tools (the inversion)
Best for: any volume where decision makers are the target.
At a certain point, scraping the website is the wrong tool. The contact page does not list the Head of Sales. The team page does not list anyone in engineering. The about page links to LinkedIn. Scraping captures what is there, which is rarely what you want.
The inversion: use a SaaS tool that takes a domain and either returns the pattern (firstname@domain, firstname.lastname@domain) or returns the verified address for a named person at that company. You skip scraping entirely and get directly to the address.
Two relevant tools:
- arrow_rightEmail Finder: enter a name and company domain, get a verified email back.
- arrow_rightEmail Pattern Detector: enter a domain, get the company's email pattern back. Generate any address you need from there.
For most B2B prospecting use cases, this path produces a cleaner list in a fraction of the time. You will scrape less, send to better addresses, and spend less time cleaning up bounces.
The cleaner alternative: domain to pattern to verify
If you remember one thing from this article, remember this sequence. It is the path most senior outbound operators use, and it produces lists with deliverability rates two to three times higher than raw scraped data.
Find the domain
Start with a company name. Confirm the right domain (acme.com vs acme.io vs acme.co) so the email you generate actually exists.
Detect the pattern
Run the domain through a pattern detector. You will get the format the company uses: firstname.lastname, firstinitial+lastname, or one of about a dozen common variants.
Generate and verify
Apply the pattern to your target's name. Verify the address with an email verifier before adding it to your campaign.
Why this beats raw scraping: scraping returns whatever is on the page (often info@ or careers@). Pattern detection returns the format used by the actual decision makers. Combine that pattern with a name from LinkedIn, an industry report, or a press release, and you have a clean targeted address without ever opening the company's website.
For a deeper breakdown of name-to-email workflows, see how to find someone's email address.
Pitfalls: where scraping breaks
Three reasons scraped lists underperform. Each has a counter-move.
Catch-all domains
Some company domains are configured to accept every address sent to them, valid or not. Send to anything@acme.com and the SMTP server says "delivered". Behind the scenes, it goes to a black hole. Your verifier returns "valid" because the server accepted it. Your campaign reports a 95 percent inbox rate that produces zero replies.
Counter-move: use a verifier that flags catch-all status. Treat catch-all addresses as a separate, lower-confidence segment. Send a smaller volume to them and watch reply rates closely.
Obfuscated emails
A site that publishes its addresses as "john [at] acme [dot] com" or as an image, or that uses JavaScript to assemble the address client-side, is signalling that it does not want to be scraped. Respect the signal. The reply rate on addresses that resist obvious scraping is lower because the recipient does not expect cold email at that inbox.
Counter-move: skip the obfuscated address. Pattern-detect the domain and find the decision maker directly.
Anti-scraping and honeypots
Larger sites watch for scraping patterns: rapid-fire requests, missing User-Agent strings, repeat hits on contact pages from a single IP. They will rate-limit you, block you, or serve a honeypot address that exists only to flag spammers. Sending to a honeypot puts your domain on a blocklist.
Counter-move: rate-limit your scraper (one to three seconds between requests), set a real User-Agent, respect robots.txt, and verify every address against multiple signals before sending. Better yet, switch to a database-backed finder so you never trigger the anti-scrape system in the first place.
Bulk: scraping hundreds of websites at once
Hand-running a Python script for 500 domains is a poor use of time. The modern bulk pattern uses three components glued together with a workflow tool.
Input: a domain list
A Google Sheet, an Airtable base, a CSV. Each row is a company domain (and optionally a target name or job title).
Engine: an email-finder API
For each domain, the workflow calls a finder API that returns the address and pattern. This replaces the scraping step entirely and works against decision makers, not whoever is published on the contact page.
Filter: an email verifier API
Pipe the returned address through a verifier. Only addresses that come back as deliverable land in the output. Run the rest into a "review" pile.
The n8n / Make workflow
In n8n or Make, this is a four-node flow: trigger on a new row in your sheet, call the finder API, call the verifier API, write the result back to the sheet. About 20 minutes of setup, then it runs forever. Push 500 domains in at 9am, walk away, come back to a clean list at noon.
The same pattern works with Zapier, Pipedream, or a custom backend. The point is that the heavy lift (scraping, parsing, deduping, verifying) is offloaded to APIs that do it better than a one-off script.
Verifying scraped emails before sending
This is the step that separates a campaign that hits the inbox from a campaign that gets flagged as spam. Sending to a list with a 15 percent bounce rate is a fast way to ruin your sender reputation, get the sending domain blocklisted, and drop future deliverability across every campaign you run.
Verification checks four things in roughly two seconds per address: syntax (is it a valid email format), domain (does the MX record exist), mailbox (does the SMTP server accept this specific address), and risk (is this a role account, disposable address, or known catch-all). Addresses that pass all four are safe to send.
Single-address verifier
For one-off checks. Paste an address, get an instant verdict.
Open the Email Verifier arrow_forwardBulk verifier
For lists of 100 to 100,000 addresses. Upload a CSV, get a clean file back with status flags per row.
Open the Bulk Verifier arrow_forwardRule of thumb: never send to a list that has not been verified within the last 30 days. Email addresses go stale at roughly 2 to 3 percent per month as people change jobs and companies update their domains.
Frequently asked questions
Is it legal to scrape emails from a website? expand_more
What is the fastest way to scrape emails from a single page? expand_more
How do I scrape emails from a website without coding? expand_more
Why are scraped emails bouncing? expand_more
Can I scrape emails from LinkedIn? expand_more
What is the difference between scraping and finding emails? expand_more
How do I scrape emails from hundreds of websites at once? expand_more
Related reading
Guide
How to find someone's email address
Two methods to find any verified business email in seconds.
Guide
How to find emails on LinkedIn
The legal way to turn a LinkedIn URL into a verified email.
Tool
Email Finder
Name plus domain in. Verified email out.
Tool
Email Pattern Detector
Domain in. The company's email format out.
Tool
Email Verifier
Single-address deliverability check in two seconds.
Tool
Bulk Email Verifier
Upload a list. Get a clean file back.