Email Validation API in JavaScript: Complete Guide

Use AI to summarize this article and ask questions

Grant Ammons
Grant Ammons – Founder April 18, 2026

Email Validation API in JavaScript: Complete Guide

Learn how to validate email addresses in JavaScript using the Truelist API. Step-by-step tutorial with code examples for Node.js and browser environments.

TL;DR: Use the Truelist API to validate emails in JavaScript with a single fetch call to https://api.truelist.io/api/v1/verify. Works in Node.js, browser environments, and as Express middleware. Pass your API key in the header and an email in the body — you get back a status, risk score, and deliverability details in JSON.

Email validation in JavaScript is one of the most effective ways to protect your sender reputation and keep your email lists clean. While regex can catch obvious formatting mistakes, it can’t tell you whether a mailbox actually exists, whether a domain accepts mail, or whether an address is a disposable throwaway. That requires a real-time API call.

In this guide, you’ll learn how to integrate the Truelist email validation API into your JavaScript applications — from simple Node.js scripts to Express middleware that validates emails at the point of entry.

Why Validate Emails in Your JavaScript Application?

If your app collects email addresses — through signup forms, lead magnets, contact forms, or checkout flows — you’re almost certainly collecting bad data. Studies show that roughly 20% of email submissions contain typos, fake addresses, or disposable emails.

Bad emails cause real damage:

  • Hard bounces spike your bounce rate and trigger ISP penalties
  • Spam traps land your domain on blacklists
  • Disposable emails inflate your list with users who will never engage
  • Typo-ridden addresses mean real customers never hear from you

Regex-based validation catches formatting errors but misses the rest. An API-based approach validates the actual mailbox by checking DNS records, SMTP responses, and known disposable email providers — all in real time.

Getting Started with the Truelist API

Before writing any code, you need a Truelist API key.

  1. Sign up at truelist.io for a free account
  2. Navigate to your dashboard and find the API section
  3. Generate an API key
  4. Store the key securely — never commit it to version control

The API endpoint for single email verification is:

POST https://api.truelist.io/api/v1/verify

Check the full API documentation for rate limits, response codes, and advanced options.

Single Email Validation in Node.js

Here’s a straightforward function that validates a single email address using Node.js fetch (available natively in Node 18+):

const TRUELIST_API_KEY = process.env.TRUELIST_API_KEY;
const TRUELIST_API_URL = 'https://api.truelist.io/api/v1/verify';

async function validateEmail(email) {
  const response = await fetch(TRUELIST_API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${TRUELIST_API_KEY}`,
    },
    body: JSON.stringify({ email }),
  });

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(
      `Truelist API error: ${response.status}${errorBody}`
    );
  }

  return response.json();
}

// Usage
async function main() {
  try {
    const result = await validateEmail('test@example.com');
    console.log('Validation result:', result);

    if (result.status === 'valid') {
      console.log('Email is deliverable');
    } else if (result.status === 'invalid') {
      console.log('Email is not deliverable — do not send');
    } else {
      console.log('Email status is uncertain — proceed with caution');
    }
  } catch (error) {
    console.error('Validation failed:', error.message);
  }
}

main();

Set your API key as an environment variable before running:

export TRUELIST_API_KEY=your_api_key_here
node validate.js

Batch Email Validation

When you need to validate a list of emails — during a list import or a scheduled cleanup — sending them one at a time is slow. Here’s a batch validation function with concurrency control:

const TRUELIST_API_KEY = process.env.TRUELIST_API_KEY;
const TRUELIST_API_URL = 'https://api.truelist.io/api/v1/verify';

async function validateEmail(email) {
  const response = await fetch(TRUELIST_API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${TRUELIST_API_KEY}`,
    },
    body: JSON.stringify({ email }),
  });

  if (!response.ok) {
    throw new Error(`API error for ${email}: ${response.status}`);
  }

  return response.json();
}

async function validateBatch(emails, concurrency = 5) {
  const results = [];
  const queue = [...emails];

  async function worker() {
    while (queue.length > 0) {
      const email = queue.shift();
      try {
        const result = await validateEmail(email);
        results.push({ email, ...result });
      } catch (error) {
        results.push({ email, status: 'error', error: error.message });
      }
    }
  }

  const workers = Array.from(
    { length: Math.min(concurrency, emails.length) },
    () => worker()
  );
  await Promise.all(workers);

  return results;
}

// Usage
async function main() {
  const emails = [
    'valid@gmail.com',
    'invalid@nonexistentdomain.xyz',
    'disposable@tempmail.com',
    'typo@gmial.com',
    'real-user@company.com',
  ];

  const results = await validateBatch(emails, 3);

  const valid = results.filter((r) => r.status === 'valid');
  const invalid = results.filter((r) => r.status === 'invalid');
  const risky = results.filter(
    (r) => r.status !== 'valid' && r.status !== 'invalid'
  );

  console.log(`Valid: ${valid.length}`);
  console.log(`Invalid: ${invalid.length}`);
  console.log(`Risky/Unknown: ${risky.length}`);
}

main();

This approach limits concurrent requests to avoid hitting rate limits while still being significantly faster than sequential validation.

Express Middleware for Real-Time Validation

For web applications, the best place to validate emails is at the point of entry — when a user submits a form. Here’s an Express middleware that validates emails before they reach your route handler:

import express from 'express';

const TRUELIST_API_KEY = process.env.TRUELIST_API_KEY;
const TRUELIST_API_URL = 'https://api.truelist.io/api/v1/verify';

async function validateEmailMiddleware(req, res, next) {
  const email = req.body.email;

  if (!email) {
    return res.status(400).json({ error: 'Email is required' });
  }

  try {
    const response = await fetch(TRUELIST_API_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${TRUELIST_API_KEY}`,
      },
      body: JSON.stringify({ email }),
    });

    if (!response.ok) {
      console.error(`Truelist API error: ${response.status}`);
      return next(); // Fail open — don't block signups if API is down
    }

    const result = await response.json();
    req.emailValidation = result;

    if (result.status === 'invalid') {
      return res.status(422).json({
        error: 'Please provide a valid email address',
        details: result.reason || 'Email address is not deliverable',
      });
    }

    next();
  } catch (error) {
    console.error('Email validation error:', error.message);
    next(); // Fail open on network errors
  }
}

const app = express();
app.use(express.json());

app.post('/api/signup', validateEmailMiddleware, (req, res) => {
  const { email } = req.body;
  const validation = req.emailValidation;

  // Email has been validated — proceed with signup
  console.log(`Signing up ${email} (status: ${validation?.status})`);

  res.json({ success: true, message: 'Account created' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Notice the “fail open” pattern: if the Truelist API is unreachable, the middleware lets the request through rather than blocking legitimate signups. You can always validate those emails later with a batch cleanup.

Handling API Responses

The Truelist API returns a JSON response with several useful fields. Here’s how to interpret and act on them:

function categorizeEmail(result) {
  const { status, reason, is_disposable, is_role_based } = result;

  // Hard reject — address does not exist
  if (status === 'invalid') {
    return {
      action: 'reject',
      message: `Invalid email: ${reason}`,
    };
  }

  // Disposable/temporary email — likely not a real user
  if (is_disposable) {
    return {
      action: 'reject',
      message: 'Disposable email addresses are not allowed',
    };
  }

  // Role-based addresses (info@, support@) — higher bounce risk
  if (is_role_based) {
    return {
      action: 'flag',
      message: 'Role-based email — may have lower engagement',
    };
  }

  // Valid and safe to send
  if (status === 'valid') {
    return {
      action: 'accept',
      message: 'Email is valid and deliverable',
    };
  }

  // Catch-all or unknown — accept but monitor
  return {
    action: 'accept_with_caution',
    message: `Email status: ${status} — monitor for bounces`,
  };
}

Key response fields to pay attention to:

  • statusvalid, invalid, unknown, or catch-all
  • reason — human-readable explanation when an email fails
  • is_disposable — whether the email is from a throwaway service
  • is_role_based — whether it’s a generic address like info@ or admin@

Error Handling and Resilience

Production applications need to handle API failures gracefully. Here’s a wrapper with retry logic and timeout handling:

async function validateWithRetry(email, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 10000);

      const response = await fetch(TRUELIST_API_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${TRUELIST_API_KEY}`,
        },
        body: JSON.stringify({ email }),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      if (response.status === 429) {
        // Rate limited — wait and retry
        const retryAfter = response.headers.get('Retry-After') || 2;
        await new Promise((r) => setTimeout(r, retryAfter * 1000));
        continue;
      }

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      if (attempt === maxRetries) {
        console.error(
          `Validation failed after ${maxRetries} attempts: ${error.message}`
        );
        return { status: 'unknown', error: error.message };
      }

      // Exponential backoff
      await new Promise((r) =>
        setTimeout(r, Math.pow(2, attempt) * 1000)
      );
    }
  }
}

This handles three common failure scenarios:

  1. Timeouts — aborts the request after 10 seconds
  2. Rate limiting — respects Retry-After headers and backs off
  3. Network errors — retries with exponential backoff before giving up

Best Practices for Email Validation in JavaScript

Validate at the point of entry. Don’t wait until you’re about to send an email to find out it’s invalid. Validate when the user submits the form — it’s a better user experience and prevents bad data from ever entering your system.

Never expose your API key on the client side. Browser-based JavaScript is visible to anyone. Always proxy validation requests through your backend server. Put the API key in server-side environment variables, never in frontend code.

Cache validation results. If the same email is submitted multiple times (password resets, login attempts), don’t burn API calls re-validating it. Cache results for 24-48 hours in Redis or even in-memory.

Fail open, not closed. If the validation API is temporarily unavailable, let the user through. You can always validate their email later. Blocking signups because of an API outage costs you real customers.

Validate in bulk on a schedule. One-time validation isn’t enough. Email addresses go bad over time — people change jobs, abandon accounts, and domains expire. Run bulk email verification against your full list periodically to keep it clean.

Handle edge cases. Catch-all domains will accept any address — you can’t definitively validate individual mailboxes on them. Role-based addresses (info@, support@) are technically valid but often have lower engagement. Build your logic to handle these nuances rather than treating everything as simply valid or invalid.

Use environment variables for configuration. Store your API key, rate limits, and timeout values in environment variables. This makes it easy to adjust settings without code changes and keeps secrets out of your repository.


Stop validating once and hoping for the best. Truelist’s recurring validation automatically re-checks your lists on a schedule — catching new bounces, dead mailboxes, and risky addresses before they damage your sender reputation. No credits, no per-email charges.

Set up recurring validation →

Ready to put Truelist
to the test?

Find out if Truelist is right for you in under 10 minutes.

Free plan available. No credit card required.