Skip to main content
Verifying leads before a campaign cuts bounce rates and protects your sender reputation. This playbook builds an n8n workflow that reads an email list, verifies every address with the Email Verifier, categorizes the results (valid, invalid, risky), and exports a clean list ready to send. If you are new to n8n with Enrow, start with the n8n getting started guide.

What will you build?

You will build a workflow that:
  1. Reads email list from Google Sheets or CSV
  2. Verifies each email using Enrow
  3. Categorizes results (valid, invalid, risky)
  4. Exports clean list ready for campaigns
Time to build: 10 minutes Difficulty: Beginner

When should you use this workflow?

Use this workflow whenever email deliverability matters to a downstream process. Common use cases:
  • Clean email lists before marketing campaigns
  • Verify form submissions in real-time
  • Maintain database hygiene
  • Reduce bounce rates

What do you need before you start?

Before building the workflow, make sure you have the following:
  • n8n (get n8n)
  • Enrow API key (get key)
  • Email list (Google Sheet or CSV)
The Enrow API key authenticates every request through the x-api-key header. See Authentication for how to retrieve and pass the API key, and Credits & billing to understand how verification credits are consumed.

How do you set up the Google Sheet?

Create a sheet with email addresses and empty columns for the results:
EmailStatusDeliverableRisk Factors
john@example.com
invalid@fake.com
temp@tempmail.com

How do you build the workflow in 5 minutes?

The fastest path is to import a ready-made workflow, configure your credentials, and run it.

Step 1: Import Workflow

Copy and import this workflow into n8n:
{
  "name": "Enrow - Email Verification",
  "nodes": [
    {
      "parameters": {},
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "operation": "read",
        "sheetId": "YOUR_SHEET_ID",
        "range": "Sheet1!A:D"
      },
      "name": "Read Email List",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 3,
      "position": [460, 300]
    },
    {
      "parameters": {
        "batchSize": 100,
        "options": {}
      },
      "name": "Batch Emails",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 1,
      "position": [680, 300]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.enrow.io/email/verify/bulk",
        "authentication": "headerAuth",
        "sendBody": true,
        "bodyContentType": "json",
        "jsonBody": "={\n  \"emails\": {{$json[\"emails\"].map(e => e.Email)}}\n}"
      },
      "name": "Verify Bulk",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [900, 300],
      "credentials": {
        "headerAuth": {"name": "Enrow API"}
      }
    },
    {
      "parameters": {
        "amount": 10,
        "unit": "seconds"
      },
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [1120, 300]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.enrow.io/email/verify/bulk?id={{$json[\"batch_id\"]}}",
        "authentication": "headerAuth"
      },
      "name": "Get Results",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [1340, 300],
      "credentials": {
        "headerAuth": {"name": "Enrow API"}
      }
    },
    {
      "parameters": {
        "operation": "update",
        "sheetId": "YOUR_SHEET_ID",
        "range": "Sheet1!A:D"
      },
      "name": "Update Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 3,
      "position": [1560, 300]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [[{"node": "Read Email List", "type": "main", "index": 0}]]
    },
    "Read Email List": {
      "main": [[{"node": "Batch Emails", "type": "main", "index": 0}]]
    },
    "Batch Emails": {
      "main": [[{"node": "Verify Bulk", "type": "main", "index": 0}]]
    },
    "Verify Bulk": {
      "main": [[{"node": "Wait", "type": "main", "index": 0}]]
    },
    "Wait": {
      "main": [[{"node": "Get Results", "type": "main", "index": 0}]]
    },
    "Get Results": {
      "main": [[{"node": "Update Sheet", "type": "main", "index": 0}]]
    }
  }
}

Step 2: Configure

  1. Replace YOUR_SHEET_ID with your Google Sheet ID
  2. Add your Enrow API key to Header Auth credential
  3. Test with a few emails first

Step 3: Run

  1. Click “Execute Workflow”
  2. Wait for verification to complete
  3. Check your Google Sheet for results

How do you build the workflow step by step?

If you prefer to build the workflow node by node, follow the detailed guide below.

Step 1: Read Email List

Google Sheets:
  1. Add “Google Sheets” node
  2. Operation: Read
  3. Select your spreadsheet
  4. Range: Sheet1!A:A (email column only)
CSV File:
  1. Add “Read Binary Files” node
  2. Add “CSV to JSON” node
  3. Map the email field

Step 2: Create Batches

For better performance, process emails in batches:
  1. Add “Split In Batches” node
  2. Batch Size: 100 (or less for faster results)
This sends 100 emails at a time to Enrow.

Step 3: Verify with Enrow Bulk API

  1. Add “HTTP Request” node
  2. Configure:
Method: POST
URL: https://api.enrow.io/email/verify/bulk
Authentication: Header Auth
  Name: x-api-key
  Value: YOUR_API_KEY

JSON Body:
{
  "emails": {{ $json["items"].map(item => item.Email) }}
}
This returns a batch_id immediately. See Verify Bulk Emails for the full request and response reference.

Step 4: Wait for Results

Add “Wait” node with 10-15 seconds delay. For large batches, wait longer. For event-driven retrieval instead of polling, pass a webhook URL in the request — see How webhooks work.

Step 5: Fetch Results

  1. Add “HTTP Request” node
  2. Configure:
Method: GET
URL: https://api.enrow.io/email/verify/bulk?id={{$json["batch_id"]}}
Authentication: Header Auth (reuse credential)
The Get Bulk Verifications endpoint returns the verification results for the batch.

Step 6: Process Results

Add “Function” node to categorize emails:
const results = $input.all()[0].json.results;

const valid = results.filter(r =>
  r.status === 'valid' &&
  r.is_deliverable &&
  !r.metadata.is_disposable
);

const invalid = results.filter(r => r.status === 'invalid');

const risky = results.filter(r =>
  r.status === 'risky' ||
  r.metadata.is_disposable ||
  r.metadata.is_role
);

return [
  {
    json: {
      total: results.length,
      valid: valid.length,
      invalid: invalid.length,
      risky: risky.length,
      validEmails: valid.map(r => r.email),
      invalidEmails: invalid.map(r => r.email),
      riskyEmails: risky.map(r => r.email)
    }
  }
];

Step 7: Update Google Sheet

  1. Add “Google Sheets” node
  2. Operation: Update
  3. Map results back to original rows
Or create separate sheets for each category:
  • Valid Emails → Sheet “Valid”
  • Invalid Emails → Sheet “Invalid”
  • Risky Emails → Sheet “Risky”

How do you verify emails from a form in real time?

To verify emails as users submit forms, trigger the workflow with a webhook and verify each address individually:

Workflow:

  1. Webhook Trigger: Receives form submissions
  2. Extract Email: Get email from form data
  3. Verify: Call Enrow /email/verify/single
  4. Decision:
    • Valid → Save to CRM (Salesforce, HubSpot, etc.)
    • Invalid → Send error response or flag for review
// In Function node after verification
if ($json.status === 'valid' && $json.is_deliverable) {
  return [
    {
      json: {
        action: 'accept',
        email: $json.email
      }
    }
  ];
} else {
  return [
    {
      json: {
        action: 'reject',
        email: $json.email,
        reason: 'Invalid or undeliverable email'
      }
    }
  ];
}
Single-address verification uses the Verify Single Email endpoint. To push accepted leads into your CRM, see the HubSpot and Salesforce integrations.

How do you export the clean list?

Once results are categorized, you can export the clean list in whichever format your stack needs.

Option 1: Google Sheets

Already done! Results are in your sheet.

Option 2: CSV Export

  1. Add “Move Binary Data” node
  2. Add “Write Binary File” node
  3. Save to disk or send via email

Option 3: Send to Email Platform

  1. Add “HTTP Request” node for your email platform API
  2. Examples:
    • Mailchimp: Add to audience
    • SendGrid: Update contacts
    • Customer.io: Sync attributes

How do you track verification metrics?

Add a summary node to compute deliverability metrics for each run:
// Function node
const results = $input.all()[0].json.results;
const summary = {
  timestamp: new Date().toISOString(),
  total_verified: results.length,
  valid: results.filter(r => r.status === 'valid').length,
  invalid: results.filter(r => r.status === 'invalid').length,
  risky: results.filter(r => r.status === 'risky').length,
  disposable: results.filter(r => r.metadata?.is_disposable).length,
  role_based: results.filter(r => r.metadata?.is_role).length,
  free_provider: results.filter(r => r.metadata?.is_free).length
};

summary.valid_rate = (summary.valid / summary.total_verified * 100).toFixed(2) + '%';
summary.invalid_rate = (summary.invalid / summary.total_verified * 100).toFixed(2) + '%';

return [{ json: summary }];
Send this summary to:
  • Slack: Post summary in channel
  • Email: Daily/weekly reports
  • Database: Track over time

How do you reduce credit usage?

Verification consumes credits per email, so trim the work before each run. Three tactics keep costs down.

Deduplicate Before Verifying

// Function node before verification
const emails = $input.all().map(item => item.json.Email);
const unique = [...new Set(emails)];

return unique.map(email => ({ json: { Email: email } }));
Removing duplicates saves credits by verifying each address only once.

Cache Results

Store verification results for 30-90 days:
// Check cache before API call
const cached = await redis.get(`verified:${email}`);
if (cached) {
  return JSON.parse(cached);
}

// Call API
const result = await verifyEmail(email);

// Cache result
await redis.setex(`verified:${email}`, 7776000, JSON.stringify(result)); // 90 days

return result;

Skip Already Verified

Only verify emails with empty status:
// IF node
{{$json["Status"]}} === "" || {{$json["Status"]}} === null
See Credits & billing for the per-endpoint credit cost.

Best Practices

Always verify your list 24-48 hours before sending. Email validity can change.
Decide based on use case:
  • B2B: Role emails (info@, support@) might be OK
  • B2C: Reject disposable emails
  • Cold outreach: Only use high-confidence emails
Track bounce rates after sending. If high, re-verify your list.
Verify your entire list every 3-6 months to remove invalid emails.

Troubleshooting

Verification taking too long?
  • Reduce batch size to 50-100 emails
  • Increase wait time between batches
Some emails showing as “risky”?
  • Check metadata.is_disposable - might be temporary emails
  • Review metadata.is_catch_all - accept-all servers
Getting rate limited?
  • Add delays between batches
  • Reduce batch size
  • Upgrade your Enrow plan
For request ceilings and 429 handling, see Rate limits. For status codes and error formats, see Error handling.

FAQ

Use the Batch Emails node to process emails in groups (the example uses a batch size of 100). For very large lists, split the work into smaller batches and add a longer wait between requests to avoid being rate limited. See Rate limits for the request ceilings.
valid means the address is deliverable, invalid means it would bounce, and risky covers catch-all, disposable, or role-based addresses where deliverability is uncertain. Use the metadata fields (is_disposable, is_role, is_catch_all, is_free) to decide how to treat risky results for your use case.
Use Verify Bulk Emails for lists and scheduled cleaning, and Verify Single Email for real-time checks such as form submissions.
Deduplicate addresses before verifying, cache results for 30-90 days, and skip rows that already have a status. See Credits & billing for per-endpoint costs.

Next steps

Email Enrichment

Find emails for your contacts with an n8n workflow.

Verify Bulk Emails

Explore the bulk verification endpoint reference.

Authentication

How to pass your API key in the x-api-key header.

Webhooks

Get notified automatically when a batch completes.