Most lead workflows I see go straight from a form, sheet or enrichment step into the CRM or a sending tool. The step that’s usually missing is checking whether the address can actually receive mail, and the one that bites people most is catch-all domains: they accept every address at the SMTP stage, so a lot of verifiers mark them “valid” and the bounces show up later.
Here’s the pattern I use. It’s 3 nodes after your trigger and works with any verifier that has an HTTP API.
1. HTTP Request (one item per lead)
- Method: POST to your verifier’s single-check endpoint
- Auth: Header Auth with your API key
- Body:
{{ JSON.stringify({ email: $json.email }) }}
- Options → Batching: 1 item per batch, ~1 s interval (verification talks to real mail servers, so a gentle pace gives cleaner answers)
2. Set
Put the original lead fields back next to the result (name, email, status, reason, confidence). The HTTP node replaces the item, and the reason is what makes the result useful later.
3. Switch on {{ $json.status }}
valid → add to sequence / CRM
catch_all or unknown → separate list for a smaller, slower send or manual review
invalid or risky → suppression list
The middle branch is the important one: if catch-alls bounce, you see it in isolation instead of dragging your verified contacts down with them.
For big lists (thousands of rows), I switch to a bulk flow: upload the list → get the price → an IF node that only continues if the price is under a limit you set (saves you from the “ran on the wrong sheet” accident) → start → Wait 1 min + status loop → download only the valid results.
Full write-up with node settings: How to Verify Email Addresses in n8n (Step by Step) | ListCheckup
Disclosure: I work on ListCheckup, which is the verifier I built this with (it returns those five statuses with a reason, and doesn’t charge for catch-all/unknown). Happy to share the two workflow JSONs if anyone wants them, and curious where others put verification: before enrichment or after?
1 個讚
Really helpful - nothing worse thank tanking your email deliverability because you’re not checking it up front. We enrich first, because it’s nice to have the person in the CRM even if they have changed employers (ie causing an email bounce).
The other step I usually see missing is some type of human-esque qualification - not every email sign up should get the same message or treatments.
1 個讚
Thanks Katie, that’s a good point. Enriching first makes sense when the record is worth keeping even after the person moves on: you keep the contact in the CRM and only the email field gets flagged. In that setup I’d run verification right after enrichment, so an old work address gets marked invalid before it reaches any sequence, and maybe trigger a re-enrichment to look for their new email.
Agree on qualification too. A Switch on lead source or company size after the verification branch is a cheap way to stop every signup getting the same treatment.
The separation between valid, catch-all, and unknown is an important detail. Treating every verifier response as simply valid or invalid can hide risks that only become visible later through bounces.
I’d also consider adding idempotency to the verification workflow, especially when retries occur after an API timeout. That helps prevent the same lead from being processed or routed multiple times.
Keeping the verification result and reason alongside the original lead data also makes troubleshooting much easier.
For bulk verification, how do you handle retries when a status-check request times out after the job has already started?
Good breakdown. To answer the question: I verify before enrichment and before anything touches the CRM. Enrichment credits are the expensive part, so there’s no point paying to enrich addresses that will bounce. One small addition: store the verification status and date as CRM properties instead of only using it for routing. That lets you re-verify older contacts (say after 6 months) and report bounce rate by lead source, which usually shows which forms or lists are feeding the junk.
Nice split. One tip for the catch-all branch: don’t dump them into the main sequence, send them in a small daily batch with a lower cap and watch bounces for a week. If a domain bounces more than ~2-3%, auto-move it to the invalid bucket and store the result on the lead record so you don’t re-verify the same address every run.
For bulk jobs I treat the verification job ID as the source of truth, not the upload. When you start the job, save the job ID plus each lead’s status in a sheet or table first, then poll status by that job ID on a loop (Wait node + HTTP Request to the status endpoint). If a status check times out, don’t re-upload the list — just re-poll the same job ID, because the job is still running server-side and re-uploading creates a duplicate job and double charges. Dedupe downloads by job ID + lead so a retried poll never appends the same lead twice. For jobs that stay stuck, route them to a small retry queue with a capped number of attempts and a final ‘manual review’ status instead of reprocessing the whole list. That way a timeout costs you one extra poll, not a full re-run.
Thanks for the detailed explanation. Treating the verification job ID as the source of truth makes a lot of sense.
I especially like the idea of retrying the same job instead of re-uploading the list, along with deduplication using job ID + lead ID.
This is a reliable approach for handling asynchronous API jobs and avoiding duplicate processing or unnecessary costs.
I work on n8n automation, API integrations, and backend reliability. I’d be happy to connect and exchange ideas around these types of workflows. If you ever need additional technical support for implementation, I’d be interested in exploring a collaboration.
Great explanation! waqarahmedofficial
1 個讚