Automation · Python · B2B Pipeline

B2B Lead Generation System

Automated B2B prospecting infrastructure — from raw domain list to scored, enriched lead in CRM with zero manual steps. Multi-source discovery, API-based enrichment, qualification scoring, and sequenced outreach running as an async Python pipeline.

ZeroManual Steps
MultiSource Discovery
ScoredQualification
AsyncPipeline
Seq'dOutreach
CRMAuto-Sync

The Problem

Root Cause

Sales teams burn hours per day on manual prospecting tasks that are structurally automatable: finding companies that match ICP, enriching contact data from multiple sources, scoring leads for fit, and queuing them for outreach. Each step is routine enough to have deterministic rules — but complex enough that no single off-the-shelf tool covers the full chain. The result is a broken manual handoff between tools that should be one pipeline.

The system treats lead generation as a data pipeline problem: structured ingestion, deterministic transformation stages, scored output. Each stage is independently replaceable. The enrichment layer is pluggable — swap API providers without touching scoring or outreach logic.

Pipeline Architecture

01
DiscoveryMulti-source prospect ingestion — domain lists, LinkedIn Sales Nav export, niche directories
02
EnrichmentAsync API enrichment — company size, tech stack, funding, decision-maker contacts
03
QualificationConfigurable scoring model — ICP fit, tech stack match, budget signals, engagement indicators
04
DeduplicationHash-based dedup against existing CRM records before insert
05
OutreachSequenced email cadence — templated, personalized with enriched data fields

Service Architecture

pipeline/discovery

Prospect Discovery

Playwright-based scraping for niche directories and job boards as ICP signals. Structured parsers for CSV exports from LinkedIn Sales Navigator and Apollo. Each source produces a normalized ProspectRaw record.

PlaywrighthttpxPydantic v2
pipeline/enrichment

Async Enrichment Layer

Async HTTP fan-out to enrichment APIs (Clearbit / Hunter / Prospeo) with exponential backoff and per-provider rate limiting. Merges partial records from multiple providers into a single enriched contact. Missing fields degrade gracefully — don't block scoring.

asynciohttpxtenacityRedis rate-limit
pipeline/scoring

Qualification Engine

Weighted scoring model over enriched fields. Configurable ICP rules in YAML — no code change to adjust weights or add signals. Outputs a QualificationScore with per-dimension breakdown so you can see why a lead ranked as it did.

PythonPyYAMLPydantic v2SQLAlchemy

Qualification Score Breakdown

Each lead exits the pipeline with a composite score and per-signal breakdown. Example output for a SaaS company in the target ICP:

SignalWeightValueOutcome
Company size (11–200 employees)25%47 employeesPASS
Tech stack match (Node / Python / React)30%3/3 matchPASS
Recent funding signal20%Seed (18mo ago)REVIEW
Decision-maker reachable15%CTO email verifiedPASS
Competitor tool usage10%No signalMISS

Architectural Decisions

ADR-001

Async Fan-out for Enrichment

Concurrent API calls per lead vs. sequential. Enrichment is I/O-bound — asyncio + httpx cuts per-lead latency from ~4s to ~0.8s when hitting 3 providers simultaneously.

ADR-002

YAML Scoring Rules

Scoring weights and ICP criteria live in config, not code. Sales ops can adjust qualification rules without a deploy. Version-controlled alongside pipeline code.

ADR-003

Graceful Enrichment Degradation

Missing enrichment data scores that dimension as zero rather than blocking the pipeline. A lead with partial enrichment still enters scoring — incomplete data is a signal, not a failure.

ADR-004

Redis for Rate Limiting

Per-provider API rate limits enforced in Redis — not in application state. Survives process restarts. Prevents accidental 429s burning API credits mid-run.

ADR-005

Pydantic v2 Throughout

Every pipeline stage has typed I/O contracts. ProspectRawEnrichedContactQualifiedLead. Schema mismatches fail loudly at stage boundaries, not silently downstream.

ADR-006

Hash-based Deduplication

SHA-256 hash of normalized domain + email checked against existing CRM records before insert. Prevents duplicate outreach without full-table scans on every insert.

Full Stack

Python 3.12asynciohttpxPlaywrightSQLAlchemy 2Pydantic v2RedistenacityPyYAMLPostgreSQLSendGridDocker

UI Screenshots

Drop PNG screenshots into images/projects/lead-gen-*.png — they'll appear here automatically.

Dashboard
Pipeline View
Scoring Output