people/match API call to pull the lead's title, LinkedIn, seniority, and employer; then a second Apollo organizations/enrich call gets headcount, revenue band, and tech stack. Claude Haiku scores the lead 0–100 against your ICP. n8n creates a HubSpot contact + company, attaches the score as a custom property, and posts a structured Slack ping to #leads-new. End-to-end runtime: ~7 seconds.
## Why this matters now — March 2026
Apollo's per-credit pricing held flat at $0.025/credit in 2026 but the new "Person+Org enrichment in one call" endpoint (rolled out Feb 2026) lets you do both lookups in a single API hit — that is two credits down to one for the same data. HubSpot's free-tier custom property limit also moved from 10 to 25 in March 2026, which is when this workflow finally became viable on the free CRM tier.
The trigger for us: our SDR Anjali calculated she was spending 1h 12m a day on enrichment grunt work. At ₹35k/month fully loaded, that is ₹2.1 lakh of her year going into copy-paste. The workflow paid for itself in 9 working days.
## The 5-step pipeline
- n8n v1.121+ (self-hosted or Cloud — workflow uses no proprietary nodes)
- Apollo.io account with API access (Basic plan at $59/mo includes 600 enrichment credits)
- HubSpot account (free tier works; Pro needed for the workflow association API)
- Anthropic API key with Claude Haiku 4.5 access
- Slack workspace with bot token, scope chat:write
- A web form that can POST JSON — we use Tally, but Typeform/Webflow/native HTML all work
https://n8n.softechinfra.com/webhook/lead-in. The first n8n node is a standard Webhook in production mode. Right after, a Set + IF combination kills three classes of garbage before they cost an Apollo credit:
1. Free email domains (gmail.com, yahoo.com, hotmail.com — about 22% of our inbound) — these almost never convert B2B, route to nurture instead of enrichment.
2. Disposable domains (mailinator.com, tempmail.org) — straight to a quarantine list.
3. Honeypot trigger — our form has a hidden "website2" field; any submission with it populated is a bot.
The gating Code node:
const FREE_DOMAINS = new Set([
'gmail.com','yahoo.com','hotmail.com','outlook.com','live.com',
'icloud.com','rediffmail.com','aol.com','protonmail.com'
]);
const DISPOSABLE = new Set([
'mailinator.com','tempmail.org','10minutemail.com','guerrillamail.com',
'throwaway.email','yopmail.com'
]);
const email = ($json.body.email || '').toLowerCase().trim();
const domain = email.split('@')[1] || '';
if ($json.body.website2) return { json: { route: 'honeypot' } };
if (DISPOSABLE.has(domain)) return { json: { route: 'spam' } };
if (FREE_DOMAINS.has(domain)) return { json: { route: 'nurture', email, domain } };
if (!email.includes('@') || !domain.includes('.')) return { json: { route: 'invalid' } };
return { json: { route: 'enrich', email, domain, ...$json.body } };route. Only route === 'enrich' triggers Apollo. This gate alone saves us ~70 Apollo credits/month — about ₹145.
## Step 2 — Apollo people enrichment
The combined enrichment endpoint (rolled out by Apollo in Feb 2026) is POST https://api.apollo.io/v1/people/match with the reveal_personal_emails=false and reveal_organization=true flags. One credit, two enrichments.
Node JSON:
{
"parameters": {
"method": "POST",
"url": "https://api.apollo.io/v1/people/match",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "x-api-key", "value": "={{ $credentials.apolloApi.apiKey }}" },
{ "name": "Content-Type", "value": "application/json" },
{ "name": "Cache-Control", "value": "no-cache" }
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"email\": \"{{ $json.email }}\",\n \"reveal_personal_emails\": false,\n \"reveal_phone_number\": false,\n \"reveal_organization\": true\n}",
"options": {
"timeout": 20000,
"retry": { "enabled": true, "maxRetries": 2 }
}
},
"id": "apollo-enrich-001",
"name": "Apollo Match Person+Org",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [680, 300]
}person.name, person.title, person.seniority, person.linkedin_url
- organization.name, organization.website_url, organization.estimated_num_employees, organization.annual_revenue_printed, organization.industry, organization.technologies (top 10)
A Set node trims the firehose down to the 14 fields HubSpot actually wants.
Apollo credit math, real numbers from our March 2026 usage:
- 312 submissions/month
- Gate kills ~88 (free email + spam + honeypot) → 224 hit Apollo
- ~7% are unmatched (Apollo returns null for the person) → 208 successful matches
- 1 credit per match × 224 attempts = 224 credits
- Apollo Basic includes 600 credits → headroom for 2.7× growth
If you outgrow Basic, the Professional plan ($99/mo) bumps you to 1,500 credits. Per-credit cost stays at ~$0.025 ≈ ₹2.07.
## Step 3 — Claude Haiku ICP scoring
We score every enriched lead 0–100 against our written ICP. The ICP rubric lives in a Notion doc that we paste into Claude's system prompt; we re-tune it monthly.
Claude system prompt (the one we actually run):
You are a B2B lead scorer for Softechinfra, an Indian IT services firm.
Score this lead 0-100 against our ICP and return strict JSON only.
ICP heavy positives:
- Indian-domiciled company, headcount 20-500
- Industries: D2C, edtech, fintech (RBI-regulated wallet/UPI), logistics, healthcare
- Title contains: CTO, VP Engineering, Head of Product, COO, Founder
- Tech stack includes: Node.js, Python, AWS, MongoDB
- Revenue band: ₹5 Cr - ₹500 Cr
ICP soft positives:
- SaaS or AI-services adjacent
- Title contains: Engineering Manager, Tech Lead
ICP negatives (cap score at 30):
- Headcount <10 or >2000
- Pure agency / consultancy (we compete)
- Personal Gmail-only lead
- Crypto-only company
Schema: { score: int 0-100, tier: 'A'|'B'|'C'|'D', reasoning: string max 200 chars, suggested_action: string max 100 chars }
Tier mapping: A=80-100, B=60-79, C=40-59, D=0-39.Lead: {{ $json.person.name }}, {{ $json.person.title }} at {{ $json.organization.name }}.
Company: {{ $json.organization.estimated_num_employees }} employees, {{ $json.organization.annual_revenue_printed }}, industry: {{ $json.organization.industry }}.
Tech stack: {{ $json.organization.technologies.slice(0,8).join(', ') }}.
Submitted message: "{{ $('Webhook').first().json.body.message }}".claude-haiku-4-5-20251115. We pin model versions explicitly — claude-haiku-latest aliases have flipped on us mid-week before.
## Step 4 — HubSpot upsert + Slack ping
Two parallel branches. HubSpot first.
The HubSpot Contact node creates-or-updates by email:
{
"parameters": {
"operation": "upsert",
"email": "={{ $json.email }}",
"additionalFields": {
"firstname": "={{ $json.person.first_name }}",
"lastname": "={{ $json.person.last_name }}",
"jobtitle": "={{ $json.person.title }}",
"linkedinurl": "={{ $json.person.linkedin_url }}",
"lifecyclestage": "lead",
"lead_source": "Web Form - Enriched",
"icp_score": "={{ $json.score }}",
"icp_tier": "={{ $json.tier }}",
"icp_reasoning": "={{ $json.reasoning }}",
"apollo_match_at": "={{ $now.toISO() }}"
}
},
"id": "hubspot-contact-001",
"name": "Upsert HubSpot Contact",
"type": "n8n-nodes-base.hubspot",
"typeVersion": 2.1,
"position": [1380, 240]
}organization.website_url as the dedup key, and finally associate the two via the HubSpot Associations API (the native node handles it in v2.1+).
Three custom HubSpot properties you have to create once: icp_score (number), icp_tier (single-line text, enum-style), apollo_match_at (datetime). The free tier allows 25 custom contact properties as of March 2026.
The Slack branch — a single Slack node posting a structured message to #leads-new:
:sparkles: New lead, tier {{ $json.tier }} ({{ $json.score }}/100)
{{ $json.person.name }} — {{ $json.person.title }} at {{ $json.organization.name }}
{{ $json.organization.estimated_num_employees }} ppl · {{ $json.organization.industry }} · {{ $json.organization.annual_revenue_printed }}
> _{{ $json.reasoning }}_
> _Action:_ {{ $json.suggested_action }}
:hubspot: tier, not Claude's free-text guess.
Mistake 4 — Webhook auth wide open. Your /lead-in endpoint should require a secret header. If the URL leaks, every script kiddie on r/sweatshop_lead_gen will pump fake submissions and you will burn through Apollo credits in a weekend.
## FAQ
### How fast is the enrichment per lead?
Median 6.4 seconds, 95th percentile 11 seconds. Apollo's API responds in ~1.5s, Claude Haiku in ~2.1s, HubSpot in ~1.3s, the rest is n8n overhead. We have run 312 leads in a single day without throttling.
### Can I use Hunter or Lusha instead of Apollo?
Yes. Swap the Apollo HTTP Request for Hunter's /v2/email-finder or Lusha's /v2/person endpoint. Hunter is cheaper (₹2/lookup) but coverage on Indian B2B is weaker — our test showed 64% match rate vs Apollo's 93%. Lusha is closer to Apollo at 88% match and ₹2.40/lookup.
### Will this work with Pipedrive or Zoho instead of HubSpot?
Yes. n8n has native nodes for both. Pipedrive's API is cleaner for custom fields; Zoho's needs you to fetch the field IDs first via the metadata endpoint and pin them in a Set node. Workflow shape is the same.
### How do I prevent duplicate HubSpot contacts?
Use upsert by email, not create. The native HubSpot node's "upsert" operation hits HubSpot's /contacts/v3/objects/contacts/upsert endpoint which deduplicates server-side. If you create-then-update via two separate operations you will race-condition under load.
### What about leads from LinkedIn ads or Meta?
Both push lead data via webhook on submission. Point them at the same /lead-in endpoint with a query param like ?source=linkedin and your Set node tags the source for downstream reporting. We have wired all three (Tally + LinkedIn + Meta) to one workflow.
### Does the workflow run when n8n is offline?
No — and that is a real failure mode. We use n8n Cloud's automatic restart, or for self-hosted we wrap the Docker container in a systemd service with Restart=always. We also send a daily "heartbeat" ping to Better Uptime from a Cron node, so a dead workflow is paged within 5 minutes.
### Is the Apollo data accurate?
Mostly. Apollo's data freshness is ~6 months on titles and company headcount in our experience. For high-stakes leads (>₹10L ARR potential) the SDR still verifies on LinkedIn before reaching out. The point is the workflow gets you from "no data" to "good-enough data" in 7 seconds; the human still owns the final call.
Want this enrichment workflow built into your CRM?
We ship the full 18-node workflow above, wired to your form, Apollo account, and HubSpot/Pipedrive/Zoho. Includes the ICP rubric tuning workshop with your sales lead. Typical cost: ₹42,000–₹68,000 depending on your CRM and form provider. Suitable if you are getting 80+ leads/month and your SDRs are losing days to copy-paste. No slides — just your problem and our honest take.
Book a 20-min Call
