I use n8n to glue scraping, enrichment and cold emailing into one repeatable workflow. This version keeps it practical: fetch prospects, normalise the data, enrich only the records worth spending money on, verify addresses, then send small batches. The node names matter less than the checks around them.
Start with capture. Use n8n’s HTTP Request node to fetch a target page or an API endpoint that lists prospects. If the page is rendered in the browser, run a headless scraper elsewhere and push JSON into n8n with a webhook. Use the HTML Extract node or a Function node with Cheerio to pull names, titles, emails and company URLs. Map the fields to a normalised schema: given_name, family_name, title, email, company, source_url. Write the results to a datastore you control: Google Sheets, Airtable, or Postgres. I keep a unique ID per row so duplicates are easy to spot.
Enrich and filter next. Call an enrichment API from n8n with the HTTP Request node. Ask for company size, industry and LinkedIn profile when available. Don’t enrich every record. Use an If node to check title keywords and company size before spending API credit. Add a simple scoring rule with a Function node: +2 for senior titles, +1 when company size is 50+, -1 for generic emails like info@. Send high-score rows to a qualified sheet. Leave the rest for later.
Verify contact quality before anything goes out. Use a syntax check on the email string. Call a verification API where you have an account, and send only addresses that pass an MX check or verification result. Track verification status on the sheet. For open tracking, create a webhook in n8n and include a 1×1 image tag in the HTML email that points to that webhook. The webhook updates the prospect row when it is hit. Use the Wait node or a Delay node to space follow-ups. Make sure the sending domain has SPF and DKIM set before sending any volume.
For cold emailing, keep the volume low and the templates short. Use the SMTP node or a Gmail node if you send from a personal account. Personalise heavily. Build templates in a Set node that insert given_name, company and a short value statement no longer than two sentences. Add a plain-text fallback. Use an If node to split follow-ups: send a second message only if the open webhook did not fire and no reply field exists. Throttle sends with a Delay or Cron node to avoid bursts. Start with a small daily volume that feels safe for the domain. Watch bounces and move bad addresses to a suppression list automatically.
Operationally, keep it boring. Add a summary webhook that runs daily and writes counts to a dashboard sheet: sent, delivered, opened, replied, bounced. Add an on-fail path to capture errors into a log sheet so selectors or API keys can be fixed quickly. Back up raw scraped JSON regularly. Rotate API keys for enrichment services if rate limits become a problem.
A few caveats are worth keeping in mind. Scraping breaks when page structure changes, so test selectors against several pages before automating. Keep templates short to avoid spam triggers. Don’t send to purchased lists without verification. Move bounces out fast.
If the flow is kept small and checked at each stage, n8n can handle prospect capture, selective enrichment, address verification and measured cold email runs without much drama. Build one block, test it with real rows, then add the next. That catches the mistakes before they get expensive.




