A B2B lead list is built in four passes: turn the ICP into filters a machine can execute, source companies from public surfaces, enrich each row into a contactable record, then verify before anything is sent. The fourth pass is the one teams skip, and it is the one that decides whether the first three were worth doing.
An ICP is a filter or it is decoration
"Mid-market SaaS companies that care about data quality" is not an ideal customer profile. It is a mood. You cannot execute it, and two people will build two different lists from it.
A usable ICP is a set of predicates, each of which can be evaluated against a public field:
- Firmographic — category or industry code, headcount band, legal form, country, city. Cheap, coarse, and the right first cut.
- Geographic — a bounded territory, because it changes which sources work. A metro area is a Maps tiling job; a country is a registry job.
- Technographic — what is on their site: a payment provider, a booking widget, a help-desk script, an absent SSL certificate. Observable from the page source, and often the strongest fit signal you can get for free.
- Signal — something that changed recently. A new office, a new job posting, a new product page. This is the predicate that turns a list into a queue.
Write them down before you collect anything. Every predicate you add after collection is a predicate you cannot backfill without re-running the whole pass.
Four public surfaces, four different lists
The same ICP produces materially different lists depending on where you look. Use more than one, deliberately.
Maps and business directories answer who exists and where. Best coverage for anything with a physical location: trades, clinics, restaurants, agencies, local B2B services. They give you name, category, address, phone and usually a domain, and almost never an email — that comes from a second pass over the domain. Details in how to scrape leads from Google Maps.
Job postings answer who is spending right now, which is a different and usually better question. A company hiring three SDRs is scaling outbound; a company hiring a data engineer with a named tool in the description has just told you its stack and its budget cycle. Hiring is the most honest public intent signal available, because it costs the company real money to emit. Building that slice is what job postings data is for.
Company websites answer are they actually a fit, and who do I contact. The pricing page tells you the segment. The footer tells you the legal entity. The script tags tell you the stack. The contact page tells you whether they publish a role mailbox or hide behind a form.
Registries and search results answer what is the legal reality — company number, legal form, filing status. This matters more than it sounds, because the legal form decides which marketing rules apply to the contact.
Run at least two surfaces and intersect them. A company that appears in Maps and is hiring and runs the technology you integrate with is a different prospect from one that merely exists.
Enrichment: turning a company into a contactable record
Sourcing gives you companies. Outreach needs records. The gap is a short crawl of each company's own site, and a few rules that keep the output honest.
- Crawl a handful of pages, not the site. Home,
/contact,/about,/team,/impressum, plus the footer. Four to six pages covers most small and mid-sized sites. - Prefer structured data. Many sites publish schema.org
OrganizationorLocalBusinessJSON-LD with email, phone and address already parsed. Parse that before you regex anything. - Take published addresses, do not invent them. A
mailto:link is a deliberate publication. A guessedfirstname.lastname@pattern is a hypothesis you are about to test on your own sending reputation. - Label role versus named mailboxes.
info@andsales@are role addresses in the sense of RFC 2142. A named mailbox is personal data with different obligations. Two columns, not one. - Store the source URL and timestamp per field. Non-negotiable, for reasons that become clear two sections down.
Mechanically this is fetch-and-extract: the Web Scraping API at $0.0002 per page if you want to own the parser, or the site contacts collector at $0.02 per site if you do not. Dedup on a stable key — a domain, or a place id where you have one — never on a company name.
Decay, with real numbers instead of a vendor statistic
Everyone repeats that B2B data decays. Almost nobody sources the figure. You can derive a defensible one from official labour statistics instead.
The US Bureau of Labor Statistics reported a total separations rate of 3.2% of employment per month in its July 2026 JOLTS release, with quits alone at 1.9%. Compound the total rate as a first-order estimate and roughly 32% of named contacts leave their job within twelve months; quits alone account for about 21%. The model is deliberately crude — churn is not uniform across roles or industries, and some seats turn over repeatedly — but it is an official number with a public methodology, which is more than the round figures in most vendor decks.
Three consequences for how you build:
- Company-level fields age far more slowly than person-level fields. A domain, address and switchboard number survive a departure; a named contact does not. Weight your list accordingly.
- A list is a subscription, not a purchase. Budget a refresh cadence at collection time. Quarterly for named contacts, annually for firmographics, is a reasonable default.
- Re-running beats patching. If your pipeline is reproducible — same filters, same sources, same dedup key — a refresh is one command and a diff. If it was a one-off export, a refresh is the whole project again.
Build or buy
Both are legitimate. The comparison that matters is not price per row.
| Bought list | Built list | |
|---|---|---|
| Time to first row | Minutes | A day or two of pipeline work |
| Fit to your ICP | Whatever the vendor's filters express | Exactly your predicates, including technographic and signal |
| Provenance per field | Usually absent | Yours, if you record it |
| Refreshability | Buy again | Re-run the pipeline |
| Exclusivity | Sold to your competitors too | Yours |
| Compliance position | You inherit collection practices you cannot inspect | You know the source of every field |
The provenance row is the one that changes the calculus. If you cannot say where a contact came from, you cannot make the disclosure that EU and UK law requires you to make — and no vendor indemnity turns that back into a compliant list.
The verification gate
Nothing leaves the pipeline unverified. This is a deliverability requirement before it is a quality one.
Google's published sender guidelines set the bar explicitly: every sender needs SPF or DKIM; senders above 5,000 messages a day to personal Gmail accounts need SPF, DKIM and DMARC with the From: domain aligned, plus one-click unsubscribe headers on marketing mail. And the threshold that ends programmes: keep the spam rate in Postmaster Tools below 0.30%, with Google recommending you stay under 0.10%.
So before send: confirm the domain resolves and has MX records, drop inferred addresses you never found published, drop role mailboxes you did not intend to mail, suppress anything on your own do-not-contact list, and ramp volume on a new sending domain gradually rather than in one batch.
Compliance is a column, not a chapter
Two rules cover most of the ground, and both are satisfied by fields you should be storing anyway.
Tell people where you got their data. GDPR Article 14 applies to personal data you did not get from the person. You must disclose your identity, the purposes and legal basis, the categories of data, their rights and — specifically — the source, including whether it was publicly accessible. Deadline: within one month, or at the first communication if you are contacting them, whichever is sooner. That is why the source URL is a column.
Know which contacts are companies and which are people. Under UK PECR the email consent rule does not apply to corporate subscribers — limited companies, LLPs, Scottish partnerships — but sole traders and ordinary partnerships count as individual subscribers and need consent or the soft opt-in. The ICO's advice when you cannot tell: treat them as individuals. In the US, CAN-SPAM requires no consent but does require accurate headers, a valid postal address and an opt-out honoured within 10 business days, with penalties up to $53,088 per email. Store legal form as a column and the rule becomes a filter rather than a judgement call at send time.
Wider context in is lead generation legal.
The pipeline, end to end
- Write the ICP as predicates. Firmographic, geographic, technographic, signal.
- Source companies from at least two public surfaces. Keep the query that produced each row.
- Dedup on a stable key. Domain or place id, never company name.
- Crawl four to six pages per domain for contact, stack and legal-form signals. Record source URL and timestamp per field.
- Score against the predicates. Everything that fails one gets a reason code, not a deletion — you will want it when the ICP changes.
- Verify: MX, published-not-inferred, role versus named, suppression list.
- Send with authentication in place and the Article 14 disclosure in the first message.
- Diff on a cadence. Quarterly for people, annually for companies.
The whole thing runs on one API key: search and Maps for discovery, page fetches for enrichment, collectors where you would rather not maintain a parser. See scrape company data for the assembled version, or market research data when the output is a dataset rather than a queue.
Sources & further reading
- US Bureau of Labor Statistics — Job Openings and Labor Turnover Summary (JOLTS), July 2026
- GDPR Article 14 — information to be provided where data have not been obtained from the data subject
- ICO — Business-to-business marketing (corporate vs individual subscribers)
- ICO — Collect information and generate leads
- FTC — CAN-SPAM Act: A Compliance Guide for Business
- Google — Email sender guidelines
- RFC 2142 — mailbox names for common services, roles and functions
- schema.org — Organization type