All posts

How to Verify Emails in n8n: HTTP Request Node, Batching and Retries

··9 min read

To verify emails in n8n, add an HTTP Request node that sends each address to an email verification API, then route the result with a Switch node. Store the API key as a Header Auth credential. Use the node's Batching option to stay under the provider's rate limit, set a timeout of about 30 seconds, and turn on Retry On Fail for rate-limit and temporary errors. Valid addresses go to your campaign, risky ones to review, and invalid ones to suppression.

This guide builds that workflow step by step, using SimpleVerifier's API as the example. The same pattern works with any verification API that takes an email and returns a status.

What you will build

The finished workflow has five nodes:

  1. Trigger: a Webhook from your form, a new row in Google Sheets, a CRM trigger, or a Schedule.
  2. Edit Fields: trims and lowercases the email.
  3. HTTP Request: calls the verification API.
  4. Switch: branches on status.
  5. Destinations: add to your sequencer or CRM, write to a review sheet, or add to a suppression list.

Verification sits between capture and use. Our overview of verifying new leads automatically explains why that position matters and compares Zapier, Make and a custom webhook. This guide covers n8n.

Step 1: Create the credential

Keep the key out of the node itself. In n8n, create a credential of the generic type Header Auth. n8n's HTTP Request node documentation lists Header Auth among the generic credential types for services without a predefined one.

Field Value
Name Authorization
Value Bearer sv_live_... (your key)

On SimpleVerifier, keys start with sv_live_ and are shown once when you create them. The API docs explain how to create one. API access comes with a subscription. Pay-as-you-go credit packs cover CSV uploads only.

Step 2: Normalise the address

Add an Edit Fields node after the trigger. Set a field email with the expression:

{{ $json.email.trim().toLowerCase() }}

Change $json.email to whatever your trigger calls the field. Normalising before the call means the same person entered twice with different capitals is checked, and stored, once.

If you want to skip obvious junk without spending a call, add an If node that only passes values containing one @ and a dot after it. Keep it loose. A strict pattern rejects real addresses, as our guide on why email regex fails shows. The API checks syntax anyway.

Step 3: Configure the HTTP Request node

The fastest way to set it up is Import cURL. n8n's docs describe the option on the Parameters tab: paste a cURL command and select Import. Paste this:

curl "https://simpleverifier.com/api/v1/verify?email=jane@company.com" \
  -H "Authorization: Bearer $SIMPLEVERIFIER_KEY"

Then adjust the imported settings:

Setting Value
Method GET
URL https://simpleverifier.com/api/v1/verify
Authentication Generic Credential Type, Header Auth, the credential from Step 1
Send Query Parameters On. Name email, value {{ $json.email }}
Send Headers Off. The credential adds the header
Options, Timeout 30000 (milliseconds)

Remove any Authorization header that the import created, so the key lives only in the credential.

The response is JSON:

{
  "email": "jane@company.com",
  "status": "valid",
  "reason": "Mailbox exists",
  "mailbox_checked": true
}

status is valid, risky or invalid. reason says why, for example "Accepts all addresses" or "Mailbox not found". The API docs list every reason.

Why the timeout matters

A live mailbox check opens an SMTP conversation with the recipient's mail server. Most answer in a few seconds. Some are slow. n8n's docs describe the Timeout option as how long the node waits for response headers before aborting. Our API docs recommend a client timeout of about 30 seconds. With a short timeout, a slow but valid answer turns into an error.

Step 4: Stay under the rate limit with Batching

Every API has a rate limit. SimpleVerifier allows 120 calls a minute per key and returns HTTP 429 above that.

The HTTP Request node has a Batching option with two fields, per n8n's docs: Items per Batch and Batch Interval (ms). n8n's rate limit guide describes this as equivalent to batching with a Loop Over Items node and a Wait node.

To stay under 120 a minute:

Items per Batch Batch Interval (ms) Requests per minute (maximum)
1 500 120
2 1000 120
1 1000 60

Leave headroom if other workflows use the same key. The limit is per key, not per workflow. If two workflows share a key, give each its own key or halve the rate.

Step 5: Retries and error handling

Two kinds of failure need different handling.

Temporary failures: 429 (rate limit) and 503 (service briefly unavailable). Retry them. In the node's Settings tab, turn on Retry On Fail. n8n's HTTP Request troubleshooting page describes setting Max Tries and Wait Between Tries (ms). The rate limit guide says that when retrying around a rate limit, the wait should be longer than the limit's window. A setting of 3 tries with a 5,000 ms wait is a sensible start.

Permanent failures: 400 (no email passed), 401 (bad key) and 402 (subscription not active). Retrying will not help. Fix the cause.

Then set On Error in the same Settings tab. n8n's node settings documentation lists three options: Stop Workflow, Continue, and Continue (using error output). Choose Continue (using error output). Failed rows then leave through a separate output, where you can write them to a "retry later" sheet. One bad row no longer stops a whole batch.

For alerts, n8n supports an error workflow that starts with an Error Trigger node and runs when an execution fails. See n8n's error handling guide.

Step 6: Route on the result

Add a Switch node after the HTTP Request. Use Rules mode on the value {{ $json.status }}:

Rule Output Typical destination
equals valid 0 Add to sequencer or CRM
equals risky 1 Review sheet, or a separate small campaign
equals invalid 2 Suppression list; tell the form user to check the address

Keep the reason with the record. "Accepts all addresses" (catch-all) and "Role-based address" call for different handling. Our guide to accept-all vs unknown results covers the risky reasons in detail.

Keeping the original fields

The HTTP Request node's output is the API response, not your original lead. It includes email, but not name or company. To use those in later nodes, reference the earlier node by name:

{{ $('Edit Fields').item.json.first_name }}

Or add a Merge node that combines the lead and the result by position.

Worked example: a form lead flow

A B2B company sends demo requests from its website to n8n.

  1. Webhook receives { "email": " Jane@Company.com ", "first_name": "Jane", "company": "Company Inc" }.
  2. Edit Fields sets email to jane@company.com.
  3. HTTP Request calls the API. The response is status: risky, reason: Accepts all addresses.
  4. Switch sends it to output 1.
  5. The risky branch creates the contact in the CRM with a property email_status = risky (catch-all) and does not enroll it in the automated sequence. A rep follows up manually.

A valid result would have gone straight into the sequence. An invalid result would have been dropped and logged, with no CRM record created.

Variant: verify new rows in a Google Sheet on a schedule

Many teams collect leads in a sheet first. The same nodes work with a different start and end:

  1. Schedule Trigger runs every hour.
  2. Google Sheets reads rows where the email_status column is empty. Filter in the node or with an If node after it.
  3. Edit Fields, HTTP Request and Switch as above.
  4. Google Sheets updates each row, matching on the email column, and writes status, reason and the date checked.

Two details make this reliable. First, write the check date. A result describes the mailbox on that day, and our re-verification cadence guide explains when it goes stale. Second, only read rows with an empty status, so a row is never charged against your rate limit twice. If you prefer formulas to workflows, the Google Sheets add-on does the same job inside the sheet.

Test before you switch it on

Run the workflow manually with five test rows before activating it:

  • An address you own, which should return valid.
  • A made-up address at gmail.com, which should return invalid. (Some providers, such as Yahoo, hide mailbox status, so pick Gmail for this test.)
  • An address at a domain you know accepts all mail, which should return risky.
  • An empty value, which should be stopped by your If node or return a 400 on the error output.
  • A value with spaces and capitals, which should arrive normalised.

Check that each lands in the right branch. Then activate.

When to use a CSV upload instead

n8n is the right tool for leads that arrive one at a time. It is a poor tool for a list you already have.

At 120 calls a minute, 10,000 addresses take about 83 minutes, and each workflow execution counts against your n8n plan or server. A CSV upload runs the same syntax, domain, disposable and mailbox checks in bulk and returns separate valid, risky and invalid files. For a one-off list, see how to remove invalid emails from a CSV. If you would rather script a recurring import than run it through n8n, our Node.js and Python SDKs include a bulk helper with automatic retries on rate limits.

Common problems

Symptom Likely cause Fix
Every call returns 401 Header value missing "Bearer " or wrong credential selected Recreate the Header Auth credential
402 on every call Subscription not active Check the plan; packs do not include API access
Bursts of 429 Batching off, or two workflows on one key Turn on Batching, split keys
Many timeouts Timeout under 30 seconds Raise to 30,000 ms
Valid leads missing name downstream HTTP output replaced the lead Reference $('Edit Fields') or merge

What it costs

On SimpleVerifier, API access is included in the Pro plan at $29.99 a month, with unlimited email verification and no per-call credits. The Email + Phone plan at $49.99 a month adds live phone carrier lookups through the same API. If you only clean a list now and then, pay-as-you-go packs cost $9 for 2,500 or $19 for 10,000 verifications by CSV upload, with credits that never expire. See pricing.

The short version

Store the key in a Header Auth credential. Normalise the email, then call the verification endpoint from an HTTP Request node with a 30-second timeout. Use Batching to stay under the rate limit, Retry On Fail for 429 and 503, and the error output so one row never stops the run. Route on status with a Switch node, keep the reason, and use CSV uploads for big lists.

Common questions

Can n8n verify email addresses?

Not on its own. n8n moves data between services. To verify an address, call an email verification API from an HTTP Request node, then branch on the status it returns.

How do I stop n8n from hitting an API rate limit?

Use the HTTP Request node's Batching option. Set Items per Batch and Batch Interval (ms) so the requests per minute stay under the provider's limit. n8n's docs describe this as equivalent to a Loop Over Items node with a Wait node. Add Retry On Fail with a wait longer than the limit's window as a backstop.

What timeout should I set for an email verification request in n8n?

About 30 seconds. A mailbox check opens an SMTP conversation with the recipient's mail server, and some servers are slow to answer. A short timeout turns slow but valid answers into errors.

What should the workflow do with risky results?

Keep them out of the main campaign and out of the trash. Risky usually means a catch-all domain, a role address or a server that would not confirm the mailbox. Send them to a review list or a separate, smaller campaign.

Is the n8n approach good for verifying a big list?

Not usually. At 120 calls a minute, 10,000 addresses take about 83 minutes and many workflow executions. A CSV upload runs the same checks in bulk and returns valid, risky and invalid files. Use n8n for new leads as they arrive.

Verify unlimited addresses for $29.99/month

Real SMTP mailbox checks. No credits, no per-email fees.

Get Started