# How I Built an Autonomous B2B Lead Enrichment & SMTP Verification Engine in Python for $0.003/Lead

> Source: <https://dev.to/reigen/how-i-built-an-autonomous-b2b-lead-enrichment-smtp-verification-engine-in-python-for-0003lead-1cga>
> Published: 2026-09-30 18:37:14+00:00

If you have ever scaled outbound campaigns beyond a few hundred contacts a week, you know the exact failure mode: commercial lead databases are stale, bulk email validators miss catch-all domains, and generic "*Hey {{FirstName}}, loved your profile*" templates land straight in the spam folder.

Most B2B automation tools force you into an expensive stack ($200+/month for enrichment, $90/month for verification, $150/month for sending tools) that still leaves you with a 4% bounce rate and burned sending domains.

In this tutorial, we will build a production-grade, asynchronous engine using **Python, `asyncio`, `aiohttp`, DNS/SMTP verification, and an LLM dual-pass pipeline** that:

`/about` or `/blog` pages.
Commercial enrichment APIs (like Apollo, ZoomInfo, or Clearbit) suffer from two structural problems:

To solve this, we need **real-time verification** at execution time and **live website extraction** rather than database lookups.

The pipeline operates across four decoupled asynchronous stages:

```
[ Raw Lead Input ] 
        │
        ▼
[ 1. DNS / MX Lookup & SMTP Handshake Simulation ] ──> (Invalid? Drop/Flag)
        │ (Valid)
        ▼
[ 2. Concurrent Async Web Scraper (aiohttp) ] ──> Fetches /, /about, /blog
        │
        ▼
[ 3. Signal Extraction & HTML Sanitization ] ──> Clean markdown/plain text
        │
        ▼
[ 4. Dual-Pass Structured LLM Engine ] ──> Strict JSON Icebreaker Payload
        │
        ▼
[ Outbound CRM / Webhook Dispatch ]
```

`aiodns`, establishes a socket connection to the mail server on port 25, sends `HELO` and `MAIL FROM`, followed by `RCPT TO`, and terminates the socket prior to issuing `DATA`.` mistral` or `llama3`) running with `response_format={"type": "json_object"}`.
We avoid triggering spam filters by never sending the actual message body. We only evaluate the SMTP response code after `RCPT TO`:

``` python
import asyncio
import aiodns
import smtplib
from typing import Tuple

async def get_mx_record(domain: str) -> str:
    resolver = aiodns.DNSResolver()
    try:
        records = await resolver.query(domain, 'MX')
        records.sort(key=lambda x: x.priority)
        return str(records[0].host)
    except Exception:
        return None

async def verify_smtp_mailbox(email: str, sender_domain: str = "verify.yourdomain.com") -> Tuple[bool, str]:
    domain = email.split("@")[1]
    mx_host = await get_mx_record(domain)

    if not mx_host:
        return False, "NO_MX_RECORD"

    # Run SMTP handshake in an async executor to avoid blocking the event loop
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(None, _raw_smtp_handshake, mx_host, email, sender_domain)

def _raw_smtp_handshake(mx_host: str, target_email: str, sender_domain: str) -> Tuple[bool, str]:
    try:
        server = smtplib.SMTP(timeout=10)
        server.connect(mx_host, 25)
        server.helo(sender_domain)
        server.mail(f"test@{sender_domain}")
        code, message = server.rcpt(target_email)
        server.quit()

        # 250: Requested mail action okay, completed
        # 550: Requested action not taken: mailbox unavailable
        if code == 250:
            return True, "DELIVERABLE"
        elif code == 550:
            return False, "MAILBOX_NOT_FOUND"
        else:
            return False, f"SMTP_CODE_{code}"
    except Exception as e:
        return False, f"ERROR: {str(e)}"
```

`aiohttp` and `BeautifulSoup`
Next, extract text from the prospect's company URL. We only retain high-signal text elements, discarding scripts, styles, navigations, and footers:

``` python
import aiohttp
from bs4 import BeautifulSoup

async def extract_site_signals(session: aiohttp.ClientSession, base_url: str) -> str:
    headers = {"User-Agent": "Mozilla/5.0 (compatible; LeadResearchBot/1.0; +https://example.com/bot)"}
    try:
        async with session.get(base_url, headers=headers, timeout=aiohttp.ClientTimeout(total=8)) as response:
            if response.status != 200:
                return ""
            html = await response.text()

        soup = BeautifulSoup(html, 'html.parser')
        for tag in soup(["script", "style", "nav", "footer", "svg", "noscript"]):
            tag.decompose()

        text = " ".join(soup.stripped_strings)
        # Truncate to first 2500 characters to stay within low context token budgets
        return text[:2500]
    except Exception:
        return ""
```

Now we take the extracted plain text and run it through an LLM to generate a bespoke cold outreach opening line. Notice the anti-fluff instructions:

``` python
from openai import AsyncOpenAI
import json

client = AsyncOpenAI(api_key="YOUR_OPENAI_API_KEY")

SYSTEM_PROMPT = """
You are an expert B2B SDR writing outbound communications.
Extract a specific technical achievement, recent initiative, or core value proposition from the company text.
Then write a personalized first-line hook.

RULES:
- Never say: 'Hope this finds you well', 'I came across your website', or 'I was impressed by'.
- Point directly to a specific fact.
- Output MUST conform to the exact JSON schema provided.
"""

async def generate_cold_hook(company_name: str, site_context: str) -> dict:
    prompt = f"""
    Target Company: {company_name}
    Scraped Context: {site_context}

    Generate the output adhering strictly to this JSON format:
    {
      "detected_core_offering": "<string>",
      "specific_detail_noticed": "<string>",
      "first_line_icebreaker": "<string>"
    }
    """

    response = await client.chat.completions.create(
        model="gpt-4o-mini", # Or your local Ollama proxy
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt}
        ],
        temperature=0.4
    )

    return json.loads(response.choices[0].message.content)
```

When running this at scale, mail servers will drop your IP if you open 50 connections at once to the same host (e.g., Google or Outlook).

Implement an `asyncio.Semaphore` along with domain grouping:

``` python
async def process_lead(sem: asyncio.Semaphore, session: aiohttp.ClientSession, lead: dict):
    async with sem:
        # 1. SMTP Check
        is_valid, reason = await verify_smtp_mailbox(lead["email"])
        if not is_valid:
            return {**lead, "status": "FAILED_VERIFICATION", "reason": reason}

        # 2. Extract Signals
        context = await extract_site_signals(session, lead["website"])
        if not context:
            return {**lead, "status": "FAILED_SCRAPING"}

        # 3. LLM Synthesis
        enrichment = await generate_cold_hook(lead["company_name"], context)

        return {
            **lead,
            "status": "READY",
            **enrichment
        }

async def main():
    concurrency_limit = asyncio.Semaphore(15) # Safe boundary
    async with aiohttp.ClientSession() as session:
        tasks = [process_lead(concurrency_limit, session, lead) for lead in leads_list]
        results = await asyncio.gather(*tasks)
        # Write results to database or push to CRM webhook
```

`gpt-4o-mini` tokens; $0.000 if using a local Ollama instance like `mistral:7b-instruct`).
You now have the blueprint to run high-volume, low-cost outbound pipelines without relying on brittle third-party providers. By combining direct async DNS/SMTP checks with targeted extraction, your domain reputation stays pristine while your cold email response rates climb.

You can implement this architecture by copying the snippets above into your custom backend, or grab the fully configured, production-tested engine complete with test fixtures, automated CRM push webhooks, and n8n integration schemas:

`EARLYBIRD` at checkout for 20% off).
Drop a comment below if you have questions about handling MX catch-all responses or fine-tuning local Ollama models for high-concurrency parsing!
