Built a B2B lead scoring and cold email workflow with Google Sheets, Apify and OpenAI

Hey everyone!! :blush:

I built an n8n workflow that works as a pay-as-you-go alternative to pricey monthly enrichment tools. It takes a raw list of company names in Google Sheets, checks if they match your ICP and have active buying signals, and only then drafts an email in Gmail.

The template just got published in the library:

What it does

  1. Queue and batching (every 30 mins):
    Reads a Google Sheet, picks up to 10 rows per run, and marks them as Processing right away so overlapping runs do not pick the same rows. Fresh leads always go first, and rows enriched in the last 30 days are skipped.

  2. Domain resolution, tech stack, and MX check:
    Resolves raw company names (Company + Country) to official domains, checks MX/SPF/DMARC records, and pulls the tech stack and public contact details using Company Domain, Tech Stack & MX/DNS Enricher.
    If a domain has no MX record, it routes it to a No MX (LinkedIn / Call Only) status instead of wasting enrichment credits on it.

  3. Parallel buying signals (hiring + paid ads):
    For verified domains with working MX, two HTTP nodes run in parallel:

  1. Separate Fit and Intent scores (0 to 100):
    Instead of lumping everything into one number, a Code node calculates Fit_Score (0-50, based on target country, tech stack match, and GTM pages) and Intent_Score (0-50, based on recent hires and active ad spend), plus a plain-text breakdown of why each lead got its score.

  2. Guarded Gmail drafts:
    Leads scoring 55+ get a short 3-sentence draft via gpt-4o-mini that references the actual signals found (and switches language if the company is in DE/FR/NL/ES/IT). It filters out privacy@, legal@, support@, and noreply@ addresses, creates the draft in Gmail first, and then updates the row status in Google Sheets.

Happy to answer any questions or hear how you handle signal scoring in your own outbound workflows

1 Like

nice, roughly how much does ~1k leads end up costing in apify credits?

Hi alux! Roughly $7 to $10 per 1k leads total (~$2.50-$3.80 for domain/tech/MX, plus ~$2.50-$3 each for the hiring and Google Ads checks since companies with 0 jobs or 0 ads cost $0). Apify’s free tier gives $5/month in credits, so the first ~500-600 leads per month are basically free

Nice that the scores come from a Code node with a plain-text breakdown. I learned that one the hard way. In a telecom pre-sales demo I built, the lead score rubric was a fixed list of points, but it lived in the prompt, so the model still did the sum. Today I’d keep exactly your split: the model only extracts the inputs, and the sum lives in code, where the rule can be tested.

One question on the queue. If a run dies after marking rows as Processing, how do they get released? A timestamp on the claim plus a sweep that resets rows stuck for more than N minutes is the usual fix, but I’m curious how you handled it in the template.

Hi! 100% - allowing an llm to perform arithmetic calculations directly within a query is a problem I don’t want to have to deal with either

In the published template I kept the node count minimal for the n8n library, so it only writes the final status (Enriched / Unresolved) at the end of the batch - if a run crashes mid-flight, the untouched rows just stay Pending and get picked up on the next 4h tick. If you add an explicit Processing claim step for overlapping runs, the timestamp + stale sweep (>15m back to Pending) you mentioned is the exact way to go, or you can route n8n’s Error Trigger workflow to flip any Processing rows from that execution back to Pending