How I verified 1,700 emails without paying for Apollo
Apollo wanted $49/month. Hunter wanted $34. Instantly wanted $37. For a bootstrapped outbound engine the tooling bill was going to cost more than the thing could plausibly earn in its first months — and every one of those products is, underneath, doing something you can do yourself with DNS and a socket.
So LeadForge's enrichment is a waterfall of free steps, ordered from most-reliable to most-speculative:
- Team-page scraping. If the company publishes founders' emails on their site or in a careers page, take that — it's ground truth.
- Pattern generation. Otherwise generate candidates:
first@domain,first.last@,f.last@— maybe eight patterns per person. - DNS-MX lookup. Before anything else, does the domain even receive mail? No MX records, discard everything.
- Live SMTP handshake. Connect to the MX host,
HELO,MAIL FROM, thenRCPT TOthe candidate — and hang up before sending anything. A250means the mailbox exists. A550means it doesn't. You never send a mail; you just ask the server politely.
Result across the pipeline: 1,744 verified emails, 525 named decision-makers, 74% contactability, $0 in data APIs.
The bug that made my numbers lie
The first version reported 90%+ contactability and I believed it for an embarrassing week. Then bounce reports started coming back on "verified" addresses. The culprit: catch-all domains. Plenty of mail servers accept RCPT TO for anything at their domain — fake-name-xyz@company.com gets a cheerful 250 just like the real address does. My handshake wasn't verifying the mailbox; it was verifying the server's willingness to lie.
The fix is almost stupid: before trusting a 250, probe the same domain with a deliberately absurd address. If zzq-not-a-real-person-8841@domain also gets accepted, the domain is catch-all and the "verification" is worthless — downgrade every pattern-generated address there to unverified.
def is_catch_all(mx_host, domain):
fake = f"zzq-not-a-real-{random_suffix()}@{domain}"
return smtp_rcpt_accepts(mx_host, fake) # 250 here = server lies
Contactability dropped from 90% to 74% overnight. That hurt to look at, but 74% was true, and the pipeline is built on a rule that keeps it honest: pattern-guessed emails that haven't survived a real handshake earn exactly zero contactability points in lead scoring. Junk data upstream becomes bounces downstream, and bounces kill sender reputation — the one asset you can't re-buy.
The boring operational details that matter
Some mail hosts tarpit you — they answer the handshake after 30 seconds to punish exactly this technique. Set aggressive timeouts and move on. Some block cloud-provider IPs entirely, so where you run the verifier matters. And rate-limit yourself per-domain; hammering one MX with fifty RCPT probes in a minute is how you end up on a blocklist, which defeats the entire point.