Skip to content

email-check-app/email-validator-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

429 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Advanced Email Validator

NPM version Build Status Downloads UNPKG TypeScript License

πŸš€ Advanced email validation library for Node.js with MX record checking, SMTP verification, disposable email detection, and much more. Now with batch processing, advanced caching, and detailed error reporting.

πŸ“‹ Table of Contents

Features

  • βœ… RFC-5321-compliant format & TLD validation
  • βœ… MX record lookup with cache
  • βœ… Live SMTP probe with multi-port walk (25 β†’ 587 β†’ 465), TLS, custom step sequences
  • βœ… Disposable + free-provider detection (10k+ domains shipped as JSON)
  • βœ… Domain typo detection + suggestions (Levenshtein + curated typo map)
  • βœ… Name extraction from local-part with composite-name support
  • βœ… WHOIS-driven domain age and registration status
  • βœ… Pluggable cache (in-memory LRU, Redis, or your own backend)
  • βœ… Verification transcript β€” opt-in structured per-step trace including the full SMTP wire-level transcript
  • βœ… parseSmtpError β€” public utility to classify a free-form SMTP error string
  • βœ… Batch email verification with concurrency control + per-error classification
  • βœ… Serverless adapters for AWS Lambda, Vercel (Edge + Node), and Cloudflare Workers/Durable Objects
  • βœ… Strict TypeScript types β€” zero any in src/

Use Cases

  • Increase delivery rate of email campaigns by removing spam emails
  • Increase email open rate and your marketing IPs reputation
  • Protect your website from spam, bots and fake emails
  • Protect your product signup form from fake emails
  • Protect your website forms from fake emails
  • Protect your self from fraud orders and accounts using fake emails
  • Integrate email address verification into your website forms
  • Integrate email address verification into your backoffice administration and order processing

API / Cloud Hosted Service

We offer this email verification and validation and more advanced features in our Scalable Cloud API Service Offering - You could try it here Email Verification


License

email-validator-js is licensed under Business Source License 1.1.

Quick License Summary

Use Case Is a commercial license required?
Exploring email-validator-js for your own research, hobbies, and testing purposes No
Using email-validator-js to build a proof-of-concept application No
Using email-validator-js to build revenue-generating applications Yes
Using email-validator-js to build software that is provided as a service (SaaS) Yes
Forking email-validator-js for any production purposes Yes

πŸ“„ For commercial licensing, visit email-check.app/license/email-validator or contact us at [email protected].


Installation

bun add @emailcheck/email-validator-js
# or
npm install @emailcheck/email-validator-js
# or
pnpm add @emailcheck/email-validator-js

Requirements (consumers)

  • Node.js >= 22 (currently-supported LTS lines: 22 Maintenance, 24 Active)
  • TypeScript >= 4.0 (for TypeScript users)

Requirements (contributing)

  • Bun >= 1.3 (test runner, package manager, dev tooling)
  • Node.js >= 24 only needed for semantic-release during the publish step

Build System

  • Rollup builds CJS + ESM bundles for the main package and the serverless entry
  • bun test for the unit + mocked-IO suite (no jest, no ts-jest)
  • Source data (common names, typo patterns, WHOIS servers) lives in src/data/*.json

Quick Start

import { verifyEmail } from '@emailcheck/email-validator-js';

// Basic usage
const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  smtpPerAttemptTimeoutMs: 3000,    // bounds a single MX Γ— port attempt
  smtpTotalDeadlineMs: 8000,        // bounds the entire SMTP probe (NEW in v5)
});

console.log(result.validFormat);  // true
console.log(result.validMx);      // true or false
console.log(result.validSmtp);    // true or false

⚠️ Breaking Change in v3.x: Enum values and constants now use camelCase instead of SCREAMING_SNAKE_CASE. See Migration Guide for details.

Migration Guide (to v3.x)

Overview

Version 3.x introduces a breaking change to improve code consistency with TypeScript/JavaScript conventions. All enum values and constants now use camelCase instead of SCREAMING_SNAKE_CASE.

What Changed

Enum Values

Before (v2.x) After (v3.x)
EmailProvider.GMAIL EmailProvider.gmail
EmailProvider.YAHOO EmailProvider.yahoo
EmailProvider.HOTMAIL_B2C EmailProvider.hotmailB2c
VerificationErrorCode.INVALID_FORMAT VerificationErrorCode.invalidFormat
VerificationErrorCode.NO_MX_RECORDS VerificationErrorCode.noMxRecords
SMTPStep.GREETING SMTPStep.greeting
SMTPStep.EHLO SMTPStep.ehlo
SMTPStep.MAIL_FROM SMTPStep.mailFrom

Constants

Before (v2.x) After (v3.x)
CHECK_IF_EMAIL_EXISTS_CONSTANTS.DEFAULT_TIMEOUT checkIfEmailExistsConstants.defaultTimeout
CHECK_IF_EMAIL_EXISTS_CONSTANTS.GMAIL_DOMAINS checkIfEmailExistsConstants.gmailDomains
WHOIS_SERVERS whoisServers

How to Migrate

Step 1: Update Enum References

// Before
import { EmailProvider, VerificationErrorCode, SMTPStep } from '@emailcheck/email-validator-js';

if (provider === EmailProvider.GMAIL) { /* ... */ }
if (error === VerificationErrorCode.INVALID_FORMAT) { /* ... */ }
const steps = [SMTPStep.GREETING, SMTPStep.EHLO, SMTPStep.MAIL_FROM];

// After
import { EmailProvider, VerificationErrorCode, SMTPStep } from '@emailcheck/email-validator-js';

if (provider === EmailProvider.gmail) { /* ... */ }
if (error === VerificationErrorCode.invalidFormat) { /* ... */ }
const steps = [SMTPStep.greeting, SMTPStep.ehlo, SMTPStep.mailFrom];

Step 2: Update Constant References

// Before
import { CHECK_IF_EMAIL_EXISTS_CONSTANTS } from '@emailcheck/email-validator-js';

const timeout = CHECK_IF_EMAIL_EXISTS_CONSTANTS.DEFAULT_TIMEOUT;
const domains = CHECK_IF_EMAIL_EXISTS_CONSTANTS.GMAIL_DOMAINS;

// After
import { checkIfEmailExistsConstants } from '@emailcheck/email-validator-js';

const timeout = checkIfEmailExistsConstants.defaultTimeout;
const domains = checkIfEmailExistsConstants.gmailDomains;

Step 3: Update Switch Statements

// Before
switch (provider) {
  case EmailProvider.YAHOO:
    // Handle Yahoo
    break;
  case EmailProvider.HOTMAIL_B2C:
    // Handle Hotmail
    break;
}

// After
switch (provider) {
  case EmailProvider.yahoo:
    // Handle Yahoo
    break;
  case EmailProvider.hotmailB2c:
    // Handle Hotmail
    break;
}

Important Notes

  1. String values remain unchanged: The underlying string values (e.g., 'gmail', 'INVALID_FORMAT') are preserved. Only the property names changed.

  2. Runtime compatibility: If you're comparing enum values to strings from external sources, the string values still work:

    // Still works in v3.x
    if (provider === 'gmail') { /* ... */ }
  3. TypeScript strict mode: Ensure you update all references before compiling, or TypeScript will report errors.

  4. Test your code: After updating, run your test suite to ensure all enum and constant references are updated correctly.

Automatic Migration

If you're using an IDE with refactoring support (like VS Code), you can use find-and-replace:

  1. Find all references to old enum values
  2. Replace with new camelCase versions
  3. Run TypeScript compiler to verify no errors

Need Help?

API Reference

Core Functions

verifyEmail(params: IVerifyEmailParams): Promise<VerificationResult>

Comprehensive email verification with detailed results and error codes.

Parameters:

  • emailAddress (string, required) β€” Email address to verify.
  • verifyMx (boolean) β€” Resolve MX records (default: true).
  • verifySmtp (boolean) β€” Run live SMTP probe (default: false).
  • checkDisposable (boolean) β€” Disposable-provider list check (default: true).
  • checkFree (boolean) β€” Free-provider list check (default: true).
  • detectName (boolean) β€” Extract first/last name from local-part (default: false).
  • suggestDomain (boolean) β€” Suggest a corrected domain on typos (default: true).
  • checkDomainAge (boolean) β€” WHOIS creation-date lookup (default: false).
  • checkDomainRegistration (boolean) β€” WHOIS registration / expiry / lock lookup (default: false).
  • skipMxForDisposable (boolean) β€” Skip MX/SMTP for disposable addresses (default: false).
  • skipDomainWhoisForDisposable (boolean) β€” Skip WHOIS for disposable addresses (default: false).
  • smtpPort (number) β€” Force a specific port for the SMTP probe (overrides the [25, 587, 465] walk).
  • SMTP time-budget controls (NEW in v5):
    • smtpPerAttemptTimeoutMs (number) β€” Per-MX Γ— port budget in ms (default: 4000).
    • smtpTotalDeadlineMs (number) β€” Hard cap on total wall-clock for the SMTP probe. Use this from a request handler with a tight latency budget. Default: unbounded.
    • smtpMaxConsecutiveFailures (number) β€” Bail after N connection-class failures in a row (connection_error / connection_timeout / connection_closed). Default: unbounded.
    • smtpMaxMxHosts (number) β€” Cap the MX walk to the first N hostnames. Default: unbounded.
    • smtpRetry ({ attempts, delayMs?, backoff? }) β€” Retry connection-class failures on the same MX Γ— port. Default: no retries.
  • SMTP envelope controls (anti-spam β€” recommended for production):
    • smtpSender (SMTPSenderStrategy) β€” Strategy for the MAIL FROM: envelope. The library default is <recipient@domain>, which blocklists key on as the textbook verification-probe fingerprint. Pick a deliberate strategy:
      • { kind: 'null-sender' } β†’ MAIL FROM:<> (RFC 5321 Β§4.5.5; DSN-shaped, best on Gmail / Outlook).
      • { kind: 'fixed', address: '[email protected]' } β†’ fixed real address (best with valid SPF / PTR / DMARC).
      • { kind: 'random-at-recipient', localPrefix? } β†’ random local-part on the recipient's domain.
      • { kind: 'random-at-domain', domain, localPrefix? } β†’ random local-part on a configured domain.
      • { kind: 'custom', build: r => ... } β†’ full escape hatch.
    • smtpHeloHostname (string) β€” Hostname presented to the MX in EHLO / HELO. Default: 'localhost' (a spam-bot signature from a public IP β€” override with a real FQDN in production).
  • whoisTimeoutMs (number) β€” Per-WHOIS-query timeout (default: 5000).
  • debug (boolean) β€” Per-line console.debug trace (default: false).
  • captureTranscript (boolean) β€” Populate result.transcript with a per-step structured trace (default: false).
  • nameDetectionMethod (function) β€” Override the default name-detection heuristic.
  • domainSuggestionMethod (function) β€” Override the default typo-suggestion heuristic.
  • commonDomains (string[]) β€” Custom canonical-domain list for the typo suggester.
  • cache (Cache) β€” Optional shared cache (MX, WHOIS, disposable / free, SMTP, domain results all stored).

Returns:

{
  email: string;
  validFormat: boolean;
  validMx: boolean | null;
  validSmtp: boolean | null;
  isDisposable: boolean;
  isFree: boolean;
  detectedName?: DetectedName | null;
  domainAge?: DomainAgeInfo | null;
  domainRegistration?: DomainRegistrationInfo | null;
  domainSuggestion?: DomainSuggestion | null;
  metadata?: {
    verificationTime: number;
    cached: boolean;
    error?: VerificationErrorCode;
  };
}

verifyEmailBatch(params: IBatchVerifyParams): Promise<BatchVerificationResult>

Verify multiple emails in parallel with concurrency control.

Parameters:

  • emailAddresses (string[], required): Array of emails to verify
  • concurrency (number): Parallel processing limit (default: 5)
  • detectName (boolean): Detect names from email addresses
  • suggestDomain (boolean): Enable domain typo suggestions
  • Other parameters from verifyEmail

Returns:

{
  results: Map<string, VerificationResult>;
  summary: {
    total: number;
    valid: number;
    invalid: number;
    errors: number;
    processingTime: number;
  };
}

Name Detection Functions

detectName(email: string): DetectedName | null

Detect first and last name from email address.

const name = detectName('[email protected]');
// Returns: { firstName: 'John', lastName: 'Doe', confidence: 0.9 }

Detection Patterns:

  • Dot separator: john.doe β†’ John Doe (90% confidence)
  • Underscore: jane_smith β†’ Jane Smith (80% confidence)
  • Hyphen: mary-johnson β†’ Mary Johnson (80% confidence)
  • CamelCase: johnDoe β†’ John Doe (70% confidence)
  • Composite names: mo1.test2 β†’ Mo1 Test2 (60% confidence)
  • Mixed alphanumeric: user1.admin2 β†’ User1 Admin2 (60% confidence)
  • Smart number handling: john.doe123 β†’ John Doe (80% confidence)
  • Contextual suffixes: john.doe.dev β†’ John Doe (70% confidence)
  • Single name: alice β†’ Alice (50% confidence)

Enhanced Features:

  • Removes email aliases (text after +)
  • Smart handling of numbers (preserves in composite names, removes trailing)
  • Recognizes contextual suffixes (dev, company, sales, years)
  • Handles complex multi-part names
  • Proper name capitalization
  • Filters out common non-name prefixes (admin, support, info, etc.)

detectNameFromEmail(params: IDetectNameParams): DetectedName | null

Advanced name detection with custom method support.

const customMethod = (email: string) => {
  // Your custom logic
  return { firstName: 'Custom', lastName: 'Name', confidence: 1.0 };
};

const name = detectNameFromEmail({
  email: '[email protected]',
  customMethod: customMethod
});

Parameters:

  • email (string): Email address
  • customMethod (function): Custom detection logic

defaultNameDetectionMethod(email: string): DetectedName | null

The default name detection implementation, exported for custom extensions.

Algorithm-Specific Name Cleaning

cleanNameForAlgorithm(name: string): string

Clean a name by removing special characters (dots, underscores, asterisks). Specifically designed for Algorithm name processing.

import { cleanNameForAlgorithm } from '@emailcheck/email-validator-js';

const cleanedName = cleanNameForAlgorithm('john.doe_smith*');
// Returns: 'johndoesmith'

const cleanedName2 = cleanNameForAlgorithm('first_name.last');
// Returns: 'firstnamelast'
detectNameForAlgorithm(email: string): DetectedName | null

Enhanced name detection for Algorithm with aggressive cleaning. Removes dots, underscores, and asterisks from detected names.

import { detectNameForAlgorithm } from '@emailcheck/email-validator-js';

const result = detectNameForAlgorithm('[email protected]');
// Returns: { firstName: 'John', lastName: 'Doesmith', confidence: 0.9025 }

// Compared to regular detection:
import { detectName } from '@emailcheck/email-validator-js';

const normalResult = detectName('[email protected]');
// Returns: { firstName: 'John', lastName: 'Doe_smith', confidence: 0.95 }

Key Differences:

  • Removes all dots (.), underscores (_), and asterisks (*) from detected names
  • Slightly reduces confidence (95% of original) due to cleaning process
  • Ideal for systems requiring clean, sanitized names without special characters
  • Normalizes multiple spaces to single spaces

Domain Suggestion Functions

suggestEmailDomain(email: string, commonDomains?: string[]): DomainSuggestion | null

Detect and suggest corrections for misspelled email domains.

const suggestion = suggestEmailDomain('[email protected]');
// Returns: { original: 'user@gmial.com', suggested: '[email protected]', confidence: 0.95 }

// With custom domain list
const customDomains = ['company.com', 'enterprise.org'];
const customSuggestion = suggestEmailDomain('[email protected]', customDomains);

Features:

  • 70+ common email domains by default
  • String similarity algorithm
  • Known typo patterns (95% confidence)
  • Smart thresholds based on domain length
  • 24-hour caching for performance

suggestDomain(params: ISuggestDomainParams): DomainSuggestion | null

Advanced domain suggestion with custom method support.

const suggestion = suggestDomain({
  domain: 'gmial.com',
  customMethod: myCustomMethod,
  commonDomains: ['company.com']
});

Parameters:

  • domain (string): Domain to check
  • customMethod (function): Custom suggestion logic
  • commonDomains (string[]): Custom domain list

defaultDomainSuggestionMethod(domain: string, commonDomains?: string[]): DomainSuggestion | null

The default domain suggestion implementation, exported for custom extensions.

isCommonDomain(domain: string, commonDomains?: string[]): boolean

Check if a domain is in the common domains list.

isCommonDomain('gmail.com'); // true
isCommonDomain('mycompany.com'); // false

// With custom list
isCommonDomain('mycompany.com', ['mycompany.com']); // true

getDomainSimilarity(domain1: string, domain2: string): number

Calculate similarity score between two domains (0-1).

getDomainSimilarity('gmail.com', 'gmial.com'); // 0.8
getDomainSimilarity('gmail.com', 'yahoo.com'); // 0.3

WHOIS Functions

Note: WHOIS functions use PSL (Public Suffix List) validation to ensure domain validity before performing lookups. Invalid domains or domains without valid TLDs will return null.

getDomainAge(domain: string, timeout?: number): Promise<DomainAgeInfo | null>

Get domain age information via WHOIS lookup.

const ageInfo = await getDomainAge('mydomain.com');
// Returns:
// {
//   domain: 'mydomain.com',
//   creationDate: Date,
//   ageInDays: 7890,
//   ageInYears: 21.6,
//   expirationDate: Date,
//   updatedDate: Date
// }

// Works with email addresses and URLs too
await getDomainAge('[email protected]');
await getDomainAge('https://mydomain.com/path');

Parameters:

  • domain (string): Domain, email, or URL to check
  • timeout (number): Timeout in milliseconds (default: 5000)

Returns: DomainAgeInfo object or null if lookup fails

getDomainRegistrationStatus(domain: string, timeout?: number): Promise<DomainRegistrationInfo | null>

Get detailed domain registration status via WHOIS.

const status = await getDomainRegistrationStatus('mydomain.com');
// Returns:
// {
//   domain: 'mydomain.com',
//   isRegistered: true,
//   isAvailable: false,
//   status: ['clientTransferProhibited'],
//   registrar: 'Example Registrar',
//   nameServers: ['ns1.mydomain.com', 'ns2.mydomain.com'],
//   expirationDate: Date,
//   isExpired: false,
//   daysUntilExpiration: 365,
//   isPendingDelete: false,
//   isLocked: true
// }

Parameters:

  • domain (string): Domain, email, or URL to check
  • timeout (number): Timeout in milliseconds (default: 5000)

Returns: DomainRegistrationInfo object or null if lookup fails

Features:

  • Supports 50+ TLDs with specific WHOIS servers
  • Automatic WHOIS server discovery for unknown TLDs
  • Parses various WHOIS response formats
  • Uses PSL (Public Suffix List) for domain validation
  • 1-hour result caching
  • Extracts domain from emails and URLs

Utility Functions

isDisposableEmail(emailOrDomain: string, cache?: ICache, options?: { skipMxCheck?: boolean; skipDomain?: boolean }): boolean

Check if email uses a disposable provider.

// Basic usage
isDisposableEmail('[email protected]'); // true
isDisposableEmail('tempmail.com'); // true
isDisposableEmail('gmail.com'); // false

// With options
isDisposableEmail('[email protected]', null, {
  skipMxCheck: true,     // Skip MX record validation
  skipDomain: true       // Skip domain validation
}); // true

isFreeEmail(emailOrDomain: string, cache?: ICache, options?: { skipMxCheck?: boolean; skipDomain?: boolean }): boolean

Check if email uses a free provider.

// Basic usage
isFreeEmail('[email protected]'); // true
isFreeEmail('yahoo.com'); // true
isFreeEmail('corporate.com'); // false

// With options
isFreeEmail('[email protected]', null, {
  skipMxCheck: true,     // Skip MX record validation
  skipDomain: true       // Skip domain validation
}); // true

isValidEmail(emailAddress: string): boolean

Validate email format (RFC 5321 compliant).

isValidEmail('[email protected]'); // true
isValidEmail('invalid.email'); // false

Validation Rules:

  • Proper @ symbol placement
  • Local part max 64 characters
  • Domain max 253 characters
  • No consecutive dots
  • No leading/trailing dots
  • Valid domain TLD

isValidEmailDomain(emailOrDomain: string): boolean

Validate if a domain has a valid TLD.

isValidEmailDomain('mydomain.com'); // true
isValidEmailDomain('example.invalid'); // false

Cache Management

import { getDefaultCache, clearDefaultCache, resetDefaultCache } from '@emailcheck/email-validator-js';

// Get the default cache instance (singleton)
const defaultCache = getDefaultCache();

// Clear all entries from the default cache
clearDefaultCache();

// Reset to a fresh cache instance
resetDefaultCache();

Types and Interfaces

DetectedName

interface DetectedName {
  firstName?: string;
  lastName?: string;
  confidence: number; // 0-1 scale
}

DomainSuggestion

interface DomainSuggestion {
  original: string;
  suggested: string;
  confidence: number; // 0-1 scale
}

NameDetectionMethod

type NameDetectionMethod = (email: string) => DetectedName | null;

DomainSuggestionMethod

type DomainSuggestionMethod = (domain: string) => DomainSuggestion | null;

DomainAgeInfo

interface DomainAgeInfo {
  domain: string;
  creationDate: Date;
  ageInDays: number;
  ageInYears: number;
  expirationDate: Date | null;
  updatedDate: Date | null;
}

DomainRegistrationInfo

interface DomainRegistrationInfo {
  domain: string;
  isRegistered: boolean;
  isAvailable: boolean;
  status: string[];
  registrar: string | null;
  nameServers: string[];
  expirationDate: Date | null;
  isExpired: boolean;
  daysUntilExpiration: number | null;
  isPendingDelete?: boolean;
  isLocked?: boolean;
}

Constants

COMMON_EMAIL_DOMAINS

Array of 70+ common email domains used for typo detection.

import { COMMON_EMAIL_DOMAINS } from '@emailcheck/email-validator-js';

console.log(COMMON_EMAIL_DOMAINS);
// ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', ...]

Includes:

  • Popular free providers (Gmail, Yahoo, Outlook, etc.)
  • Business email services (Google Workspace, Microsoft, etc.)
  • Privacy-focused providers (ProtonMail, Tutanota, etc.)
  • Regional providers (GMX, Yandex, QQ, etc.)
  • Hosting services (GoDaddy, Namecheap, etc.)

Error Codes

enum VerificationErrorCode {
  invalidFormat = 'INVALID_FORMAT',
  invalidDomain = 'INVALID_DOMAIN',
  noMxRecords = 'NO_MX_RECORDS',
  smtpConnectionFailed = 'SMTP_CONNECTION_FAILED',
  smtpTimeout = 'SMTP_TIMEOUT',
  mailboxNotFound = 'MAILBOX_NOT_FOUND',
  mailboxFull = 'MAILBOX_FULL',
  networkError = 'NETWORK_ERROR',
  disposableEmail = 'DISPOSABLE_EMAIL',
  freeEmailProvider = 'FREE_EMAIL_PROVIDER'
}

Configuration Options

timeout

Set a timeout in milliseconds for the smtp connection. Default: 4000.

verifyMx

Enable or disable domain checking. This is done in two steps:

  1. Verify that the domain does indeed exist
  2. Verify that the domain has valid MX records

Default: false.

verifySmtp

Enable or disable mailbox checking. Only a few SMTP servers allow this, and even then whether it works depends on your IP's reputation with those servers. This library performs a best effort validation:

  • It returns null for Yahoo addresses, for failed connections, for unknown SMTP errors
  • It returns true for valid SMTP responses
  • It returns false for SMTP errors specific to the address's formatting or mailbox existence

Default: false.

checkDisposable (NEW)

Check if the email domain is a known disposable email provider. Default: false.

checkFree (NEW)

Check if the email domain is a known free email provider. Default: false.

detailed (NEW)

Return detailed verification results with error codes. Default: false.

retryAttempts (NEW)

Number of retry attempts for transient failures. Default: 1.

Examples

Basic Usage

import { verifyEmail } from '@emailcheck/email-validator-js';

const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  smtpPerAttemptTimeoutMs: 3000,
});
console.log(result.validFormat);  // true
console.log(result.validMx);      // true
console.log(result.validSmtp);    // true

Detailed Verification (NEW)

import { verifyEmail } from '@emailcheck/email-validator-js';

const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  checkDisposable: true,
  checkFree: true
});
// result.validFormat: true
// result.validMx: true
// result.validSmtp: true
// result.isDisposable: false
// result.isFree: false
// result.metadata.verificationTime: 125

Batch Verification (NEW)

import { verifyEmailBatch } from '@emailcheck/email-validator-js';

const emails = ['[email protected]', '[email protected]', '[email protected]'];

const result = await verifyEmailBatch({
  emailAddresses: emails,
  concurrency: 5,
  verifyMx: true,
  checkDisposable: true,
  checkFree: true
});
// result.summary.valid: 2
// result.summary.invalid: 1
// result.summary.processingTime: 234

Enhanced SMTP Verification (NEW)

import { verifyMailboxSMTP, getDefaultCache } from '@emailcheck/email-validator-js';

// Direct SMTP probe β€” caller already has resolved MX records.
const { smtpResult, port, cached, portCached } = await verifyMailboxSMTP({
  local: 'user',
  domain: 'example.com',
  mxRecords: ['mx.example.com'],
  options: {
    ports: [25, 587, 465],         // Plain β†’ STARTTLS-able β†’ implicit-TLS
    perAttemptTimeoutMs: 5000,     // Per-MX Γ— port budget (renamed from `timeout` in v5)
    totalDeadlineMs: 12_000,       // Hard cap on total wall-clock (NEW in v5)
    maxConsecutiveFailures: 3,     // Bail after N connection-class failures (NEW in v5)
    cache: getDefaultCache(),      // Per-isolate verdict + port cache
    debug: false,
    tlsConfig: {                   // Renamed from `tls` in v5
      rejectUnauthorized: false,
      minVersion: 'TLSv1.2',
    },
    heloHostname: 'your-domain.com', // EHLO/HELO identity (renamed from `hostname` in v5)
    startTls: 'auto',              // STARTTLS upgrade on plaintext ports (NEW in v5)
    pipelining: 'auto',            // Use SMTP PIPELINING when advertised
    captureTranscript: false,      // See "SMTP Transcript Capture" below
  },
});

console.log(`SMTP result: ${smtpResult.isDeliverable} via port ${port}`);
console.log(`canConnectSmtp=${smtpResult.canConnectSmtp}, error=${smtpResult.error ?? 'none'}`);

Configuration presets (NEW in v5)

Don't want to think about timeouts and retries? Pick a preset that matches your deployment shape:

import { verifyEmail, VERIFY_EMAIL_PRESETS } from '@emailcheck/email-validator-js';

// Lambda / Vercel / Cloudflare-Workers handler
await verifyEmail({
  emailAddress: '[email protected]',
  verifySmtp: true,
  ...VERIFY_EMAIL_PRESETS.serverless,
});

// Long-running worker / dyno
await verifyEmail({ ..., ...VERIFY_EMAIL_PRESETS.dedicated });

// Bulk processing
await verifyEmail({ ..., ...VERIFY_EMAIL_PRESETS.batch });

// Form-autocomplete UX (sub-3s)
await verifyEmail({ ..., ...VERIFY_EMAIL_PRESETS.fast });
Preset Per-attempt Total deadline Max consecutive failures Max MX Retry
serverless 2500 ms 5 s 3 2 none β€” fail fast
dedicated 5000 ms 30 s unbounded unbounded 1 retry, 500 ms exp backoff
batch 10 000 ms 60 s unbounded unbounded 2 retries, 1 s exp backoff
fast 1500 ms 3 s 2 1 none β€” fail fast

SMTP_PRESETS is a parallel set with the unprefixed field names for verifyMailboxSMTP({ options }) callers β€” same values, same shape.

You can spread + override:

await verifyEmail({
  emailAddress: '[email protected]',
  ...VERIFY_EMAIL_PRESETS.serverless,
  smtpTotalDeadlineMs: 3000, // tighter than the preset's 5s
});

Time-budget controls (NEW in v5)

The probe walks mxRecords Γ— ports (worst-case 4 Γ— 3 = 12 attempts at 3s each = 36s). Four orthogonal knobs let you bound that:

import { verifyEmail, verifyMailboxSMTP } from '@emailcheck/email-validator-js';

await verifyEmail({
  emailAddress: '[email protected]',
  verifySmtp: true,
  smtpPerAttemptTimeoutMs: 3000,    // Bound a single MX Γ— port attempt
  smtpTotalDeadlineMs: 5000,        // Hard cap on total wall-clock
  smtpMaxConsecutiveFailures: 3,    // Bail after 3 connection failures in a row
  smtpMaxMxHosts: 2,                // Try only the first 2 MXes
  smtpRetry: {                      // Retry connection-class failures
    attempts: 1,
    delayMs: 200,
    backoff: 'exponential',         // or 'fixed'
  },
});

// On verifyMailboxSMTP directly, the same knobs are unprefixed:
await verifyMailboxSMTP({
  local: 'alice', domain: 'example.com', mxRecords: [...],
  options: {
    perAttemptTimeoutMs: 3000,
    totalDeadlineMs: 5000,
    maxConsecutiveFailures: 3,
    maxMxHosts: 2,
    retry: { attempts: 1, delayMs: 200, backoff: 'exponential' },
  },
});
Knob Default When to use
(smtp)PerAttemptTimeoutMs 4000 (verifyEmail) / 3000 (verifyMailboxSMTP) Per-MX Γ— port budget. Bound a single attempt.
(smtp)TotalDeadlineMs unbounded Hard wall-clock cap. Use from a request handler with a tight latency budget.
(smtp)MaxConsecutiveFailures unbounded Cut off probes when the network path is dead. Counter resets on any non-connection-class outcome.
(smtp)MaxMxHosts unbounded Cap the MX walk regardless of how many DNS returned.
(smtp)Retry no retries Retry connection-class failures on the same MX Γ— port. Definitive answers (250 / 550 / 552) are never retried.

PerAttemptTimeout and TotalDeadline are orthogonal β€” use both when you have both a per-attempt SLO and a hard caller-side budget.

Custom SMTP step sequence

Override the default greeting β†’ EHLO β†’ MAIL FROM β†’ RCPT TO walk for advanced cases:

import { verifyMailboxSMTP, SMTPStep } from '@emailcheck/email-validator-js';

const { smtpResult } = await verifyMailboxSMTP({
  local: 'user',
  domain: 'example.com',
  mxRecords: ['mx.example.com'],
  options: {
    sequence: {
      steps: [SMTPStep.greeting, SMTPStep.helo, SMTPStep.mailFrom, SMTPStep.rcptTo],
      from: '<[email protected]>',  // Custom MAIL FROM payload
    },
    ports: [587, 465],
  },
});

SMTP Transcript Capture

Set captureTranscript: true to get the full server reply log and command sequence on the result. Useful for debugging delivery quirks or building admin UIs:

import { verifyMailboxSMTP } from '@emailcheck/email-validator-js';

const { smtpResult } = await verifyMailboxSMTP({
  local: 'user',
  domain: 'example.com',
  mxRecords: ['mx.example.com'],
  options: { ports: [25, 587], perAttemptTimeoutMs: 5000, captureTranscript: true },
});

// Both arrays aggregate across every MX Γ— port attempted, prefixed:
//   "mx.example.com:25|s| 220 mx.example.com ESMTP"
//   "25|c| EHLO localhost"
console.log(smtpResult.transcript);
console.log(smtpResult.commands);

For verification across the entire pipeline (syntax / disposable / free / MX / SMTP / WHOIS / name / suggestion), enable captureTranscript on verifyEmail to get a structured per-step trace β€” see Verification Transcript below.

Running Examples

Examples are grouped by topic under examples/. See examples/README.md for the full index.

# Bun runs TS directly β€” no compilation step
bun run examples/smtp/usage.ts
bun run examples/smtp/enhanced.ts
bun run examples/cache/custom-memory.ts
bun run examples/high-level/advanced-usage.ts
bun run examples/integrations/algolia.ts

After installation in your own project (Node 22+):

node --experimental-strip-types examples/smtp/usage.ts

Name Detection (ENHANCED)

import { detectName, verifyEmail } from '@emailcheck/email-validator-js';

// Standalone name detection - now with composite name support
const name = detectName('[email protected]');
// name: { firstName: 'John', lastName: 'Doe', confidence: 0.9 }

// Handle alphanumeric composite names
const composite = detectName('[email protected]');
// composite: { firstName: 'Mo1', lastName: 'Test2', confidence: 0.6 }

// Smart handling of numbers and suffixes
const withNumbers = detectName('[email protected]');
// withNumbers: { firstName: 'John', lastName: 'Doe', confidence: 0.8 }

const withSuffix = detectName('[email protected]');
// withSuffix: { firstName: 'Jane', lastName: 'Smith', confidence: 0.7 }

// Integrated with email verification
const result = await verifyEmail({
  emailAddress: '[email protected]',
  detectName: true
});
// result.detectedName: { firstName: 'Jane', lastName: 'Smith', confidence: 0.8 }

// Custom detection method
const customMethod = (email: string) => {
  // Your custom logic here
  return { firstName: 'Custom', lastName: 'Name', confidence: 1.0 };
};

const resultCustom = await verifyEmail({
  emailAddress: '[email protected]',
  detectName: true,
  nameDetectionMethod: customMethod
});

Domain Typo Detection (NEW)

import { suggestEmailDomain, verifyEmail } from '@emailcheck/email-validator-js';

// Standalone domain suggestion
const suggestion = suggestEmailDomain('[email protected]');
// suggestion: { original: 'user@gmial.com', suggested: '[email protected]', confidence: 0.95 }

// Integrated with email verification (enabled by default in detailed mode)
const result = await verifyEmail({
  emailAddress: '[email protected]',
  suggestDomain: true  // Default: true for detailed verification
});
// result.domainSuggestion: { original: 'john@yaho.com', suggested: '[email protected]', confidence: 0.9 }

// With custom domain list
const customDomains = ['company.com', 'enterprise.org'];
const resultCustom = await verifyEmail({
  emailAddress: '[email protected]',
  suggestDomain: true,
  commonDomains: customDomains
});
// resultCustom.domainSuggestion: { suggested: 'user@company.com', confidence: 0.85 }

Handling Different Validation Scenarios

When a domain does not exist or has no MX records:

const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true
});
// result.validFormat: true (format is valid)
// result.validMx: false (no MX records)
// result.validSmtp: null (couldn't be performed)

Using Detailed Verification for Better Insights

const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  checkDisposable: true,
  checkFree: true
});

if (!result.validFormat) {
  console.log('Invalid email format');
} else if (!result.validMx) {
  console.log('Invalid domain - no MX records');
} else if (result.isDisposable) {
  console.log('Disposable email detected');
} else if (result.metadata?.error) {
  switch (result.metadata.error) {
    case VerificationErrorCode.disposableEmail:
      console.log('Rejected: Disposable email');
      break;
    case VerificationErrorCode.noMxRecords:
      console.log('Rejected: Invalid domain');
      break;
    case VerificationErrorCode.mailboxNotFound:
      console.log('Rejected: Mailbox does not exist');
      break;
  }
}

Batch Processing for Large Lists

const emails = [
  '[email protected]',
  '[email protected]',
  '[email protected]',
  // ... hundreds more
];

const batch = await verifyEmailBatch({
  emailAddresses: emails,
  concurrency: 10, // Process 10 emails simultaneously
  verifyMx: true,
  checkDisposable: true,
  detailed: true
});

console.log(`Processed ${batch.summary.total} emails`);
console.log(`Valid: ${batch.summary.valid}`);
console.log(`Invalid: ${batch.summary.invalid}`);
console.log(`Time: ${batch.summary.processingTime}ms`);

// Filter out invalid emails
const validEmails = [];
for (const [email, result] of batch.results) {
  if (result.validFormat) {
    validEmails.push(email);
  }
}

Performance Optimization with Caching

// First verification - hits DNS and SMTP
const first = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true
});
// Takes ~500ms

// Second verification - uses cache
const second = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true
});
// Takes ~1ms (cached)

// Clear cache if needed
clearAllCaches();

πŸ’» Command-line Tool (email-validate)

The package ships an email-validate binary that runs the full validation pipeline against one address, captures a structured transcript, prints the result to stdout, and saves the JSON result to ./logs/ by default.

Run without installing (npx / bunx / pnpm dlx)

# One-off check β€” pulls the latest published version, no install required
npx -p @emailcheck/email-validator-js email-validate [email protected]

# Same with bunx / pnpm dlx
bunx -p @emailcheck/email-validator-js email-validate [email protected]
pnpm dlx -p @emailcheck/email-validator-js email-validate [email protected]

# Pin a version (avoids npx caching surprises in CI)
npx -p @emailcheck/[email protected] email-validate [email protected]

The -p <package> form is the safest because the bin name (email-validate) differs from the package name. The shorthand npx @emailcheck/email-validator-js [email protected] also works since the package has exactly one bin.

Install globally

bun add -g @emailcheck/email-validator-js
# or: npm i -g @emailcheck/email-validator-js
# or: pnpm add -g @emailcheck/email-validator-js

Examples

# Quick interactive check β€” full pipeline, pretty colored output
email-validate [email protected]

# Skip the SMTP probe (fast, just format / MX / lists / typos)
email-validate [email protected] --no-smtp

# Add WHOIS age + registration for full domain reputation picture
email-validate [email protected] --whois-age --whois-registration

# Pipe JSON through jq
email-validate [email protected] --format json --quiet --no-log-file | jq

# Use the exit code in shell scripts (0 = ok, 1 = undeliverable / invalid)
if email-validate "$EMAIL" --quiet --no-log-file > /dev/null; then
  echo "good email"
fi

# Pin to a single SMTP port + custom HELO + custom log path
email-validate [email protected] --port 587 --hostname mta.acme.com --log-dir /var/log/email

# Debug a delivery quirk β€” full transcript + console logs
email-validate [email protected] --debug --format pretty

Defaults

The CLI uses interactive-friendly defaults different from the library defaults (which favor speed over thoroughness for batch use):

Flag CLI default Library default
--smtp on off
--detect-name on off
--whois-age / --whois-registration off off
--captureTranscript on off
--log-dir ./logs n/a

The default config writes a JSON result to ./logs/email-validate-<timestamp>-<email>.json after every run.

Programmatic CLI

The CLI parser, formatter, and runner are also exported as a module so you can embed email-validate semantics in your own tooling:

import { parseArgs, run } from '@emailcheck/email-validator-js/cli';

const parsed = parseArgs(['[email protected]', '--no-smtp', '--format', 'json']);
if (parsed.kind === 'args') {
  const exitCode = await run(parsed);
  process.exit(exitCode);
}

Run email-validate --help to see every flag, or read examples/cli-usage.md for end-to-end recipes.

πŸš€ Custom Cache Injection

The library supports parameter-based cache injection, allowing you to use custom cache backends like Redis, Memcached, or any LRU-compatible cache implementation.

πŸ“¦ Performance & Caching

The library includes a built-in LRU cache for all operations. By default, it uses a lazy-loaded singleton cache instance.

Default Cache Usage

import { verifyEmail } from '@emailcheck/email-validator-js';

// No cache setup needed - uses default LRU cache automatically
const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true
});

// Subsequent calls with the same email will use cached results
const result2 = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true
});

Custom Cache Implementation

Create your own cache by implementing the ICache interface:

import { verifyEmail, type ICache, ICacheStore, DEFAULT_CACHE_OPTIONS } from '@emailcheck/email-validator-js';
import { LRUAdapter } from '@emailcheck/email-validator-js';

// Create custom cache with LRU adapters
const customCache: ICache = {
  mx: new LRUAdapter<string[]>(DEFAULT_CACHE_OPTIONS.maxSize.mx, DEFAULT_CACHE_OPTIONS.ttl.mx),
  disposable: new LRUAdapter<boolean>(DEFAULT_CACHE_OPTIONS.maxSize.disposable, DEFAULT_CACHE_OPTIONS.ttl.disposable),
  free: new LRUAdapter<boolean>(DEFAULT_CACHE_OPTIONS.maxSize.free, DEFAULT_CACHE_OPTIONS.ttl.free),
  domainValid: new LRUAdapter<boolean>(DEFAULT_CACHE_OPTIONS.maxSize.domainValid, DEFAULT_CACHE_OPTIONS.ttl.domainValid),
  smtp: new LRUAdapter<boolean | null>(DEFAULT_CACHE_OPTIONS.maxSize.smtp, DEFAULT_CACHE_OPTIONS.ttl.smtp),
  domainSuggestion: new LRUAdapter<{ suggested: string; confidence: number } | null>(
    DEFAULT_CACHE_OPTIONS.maxSize.domainSuggestion,
    DEFAULT_CACHE_OPTIONS.ttl.domainSuggestion
  ),
  whois: new LRUAdapter<any>(DEFAULT_CACHE_OPTIONS.maxSize.whois, DEFAULT_CACHE_OPTIONS.ttl.whois),
};

// Use with email verification
const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  cache: customCache  // Pass the cache instance
});

Redis Cache Implementation

import { verifyEmail, type ICache, ICacheStore } from '@emailcheck/email-validator-js';
import { RedisAdapter } from '@emailcheck/email-validator-js';
import Redis from 'ioredis';

// Create Redis client
const redis = new Redis({
  host: 'localhost',
  port: 6379,
});

// Create Redis cache
const redisCache: ICache = {
  mx: new RedisAdapter(redis, {
    keyPrefix: 'email:mx:',
    ttl: 1800000, // 30 minutes
  }),
  disposable: new RedisAdapter(redis, {
    keyPrefix: 'email:disposable:',
    ttl: 86400000, // 24 hours
  }),
  free: new RedisAdapter(redis, {
    keyPrefix: 'email:free:',
    ttl: 86400000, // 24 hours
  }),
  domainValid: new RedisAdapter(redis, {
    keyPrefix: 'email:domain:',
    ttl: 86400000, // 24 hours
  }),
  smtp: new RedisAdapter(redis, {
    keyPrefix: 'email:smtp:',
    ttl: 1800000, // 30 minutes
  }),
  domainSuggestion: new RedisAdapter(redis, {
    keyPrefix: 'email:suggest:',
    ttl: 86400000, // 24 hours
  }),
  whois: new RedisAdapter(redis, {
    keyPrefix: 'email:whois:',
    ttl: 3600000, // 1 hour
  }),
};

// Use with batch verification
import { verifyEmailBatch } from '@emailcheck/email-validator-js';

const batchResult = await verifyEmailBatch({
  emailAddresses: ['[email protected]', '[email protected]'],
  verifyMx: true,
  verifySmtp: true,
  cache: redisCache,
  concurrency: 10
});

Custom Cache Store Implementation

Create your own cache adapter by implementing the ICacheStore interface:

import { verifyEmail, type ICacheStore } from '@emailcheck/email-validator-js';

class MyCustomCache<T> implements ICacheStore<T> {
  private store = new Map<string, { value: T; expiry: number }>();

  async get(key: string): Promise<T | null> {
    const item = this.store.get(key);
    if (!item) return null;

    if (Date.now() > item.expiry) {
      this.store.delete(key);
      return null;
    }

    return item.value;
  }

  async set(key: string, value: T, ttlMs?: number): Promise<void> {
    const expiry = Date.now() + (ttlMs || 3600000);
    this.store.set(key, { value, expiry });
  }

  async delete(key: string): Promise<boolean> {
    return this.store.delete(key);
  }

  async has(key: string): Promise<boolean> {
    const item = this.store.get(key);
    if (!item) return false;

    if (Date.now() > item.expiry) {
      this.store.delete(key);
      return false;
    }

    return true;
  }

  async clear(): Promise<void> {
    this.store.clear();
  }

  size(): number {
    return this.store.size;
  }
}

// Use custom cache store
const customCache = {
  mx: new MyCustomCache<string[]>(),
  disposable: new MyCustomCache<boolean>(),
  free: new MyCustomCache<boolean>(),
  domainValid: new MyCustomCache<boolean>(),
  smtp: new MyCustomCache<boolean | null>(),
  domainSuggestion: new MyCustomCache<{ suggested: string; confidence: number } | null>(),
  whois: new MyCustomCache<any>(),
};

const result = await verifyEmail({
  emailAddress: '[email protected]',
  cache: customCache
});

Cache Options

Default cache TTL and size settings:

import { DEFAULT_CACHE_OPTIONS } from '@emailcheck/email-validator-js';

// TTL (Time To Live) in milliseconds
DEFAULT_CACHE_OPTIONS.ttl = {
  mx: 3600000,              // 1 hour
  disposable: 86400000,      // 24 hours
  free: 86400000,           // 24 hours
  domainValid: 86400000,    // 24 hours
  smtp: 1800000,            // 30 minutes
  domainSuggestion: 86400000, // 24 hours
  whois: 3600000,           // 1 hour
};

// Maximum number of entries per cache type
DEFAULT_CACHE_OPTIONS.maxSize = {
  mx: 500,
  disposable: 1000,
  free: 1000,
  domainValid: 1000,
  smtp: 500,
  domainSuggestion: 1000,
  whois: 200,
};

🌐 Serverless Deployment

The package ships a serverless build (@emailcheck/email-validator-js/serverless/*) that runs without node:net / node:dns / node:tls. It targets:

  • AWS Lambda β€” API Gateway, direct invocation, routed handler
  • GCP Cloud Functions (2nd gen) β€” Express-style (req, res) on Cloud Run
  • Vercel β€” Edge Functions and Node.js runtime
  • Cloudflare Workers β€” including KV write-through and Durable Objects
  • Netlify Functions β€” Lambda-shaped event with redirect-aware path stripping
  • Azure Functions (v4 model) β€” Web-API-shaped HTTP triggers
  • Netlify Edge Functions / Deno Deploy β€” direct validateEmailCore use

AWS Lambda (routed handler)

// Routed: GET /health, POST /validate, POST /validate/batch
export { handler } from '@emailcheck/email-validator-js/serverless/aws';

Other shapes available: apiGatewayHandler (legacy, no path routing) and lambdaHandler (direct invocation).

Vercel Edge Functions

// app/api/validate/route.ts
import { handler } from '@emailcheck/email-validator-js/serverless/vercel';

export const runtime = 'edge';
export async function POST(request: Request) { return handler(request); }

Other shapes: edgeHandler (no routing) and nodeHandler (Express-style).

Cloudflare Workers

// src/worker.ts
export { default } from '@emailcheck/email-validator-js/serverless/cloudflare';

Bind a EMAIL_CACHE KV namespace in wrangler.toml to get write-through caching across instances. Bind EMAIL_VALIDATOR as a Durable Object (class EmailValidatorDO, also exported) for stateful validation with /validate, /cache/clear, /cache/stats.

GCP Cloud Functions (2nd gen)

import { gcpHandler } from '@emailcheck/email-validator-js/serverless/gcp';
export const validateEmail = gcpHandler;

Deploy with gcloud functions deploy --gen2 --runtime=nodejs20 --trigger-http. See SERVERLESS.md for the Functions Framework integration and Cloud Run usage.

Netlify Functions

// netlify/functions/validate.ts
export { netlifyHandler as handler } from '@emailcheck/email-validator-js/serverless/netlify';

The adapter strips /.netlify/functions/<name> and /api/* prefixes automatically, so the same handler works whether you hit the raw function URL or a redirect.

Azure Functions (v4)

import { app } from '@azure/functions';
import { azureHandler } from '@emailcheck/email-validator-js/serverless/azure';

app.http('validateEmail', {
  methods: ['GET', 'POST', 'OPTIONS'],
  route: '{*path}',
  handler: azureHandler,
});

Edge MX support via the built-in DoH resolver

A built-in DoHResolver ships with the package β€” works in any runtime with fetch (Cloudflare Workers, Vercel Edge, Deno, browsers, Node 22+):

import {
  validateEmailCore,
  DoHResolver,
} from '@emailcheck/email-validator-js/serverless/verifier';

const result = await validateEmailCore('[email protected]', {
  validateMx: true,
  dnsResolver: new DoHResolver(),  // defaults to Cloudflare 1.1.1.1
});
// result.validators.mx === { valid: true, records: ['mx.example.com'] }

Configurable: pass { endpoint, timeoutMs, fetch } to point at Google/NextDNS/self-hosted, tune the per-query timeout, or inject a custom fetch. Compatible with cf-doh β€” see SERVERLESS.md for the full breakdown.

What works in serverless mode

Capability Edge Notes
Syntax validation βœ… RFC-pragmatic regex
Typo detection / suggestions βœ… Same data as Node API
Disposable detection βœ… Full list bundled
Free-provider detection βœ… Full list bundled
MX records βœ…ΒΉ Requires dnsResolver injection
SMTP probe ❌ Needs raw TCP β€” Node-only
WHOIS lookups ❌ Needs raw TCP β€” Node-only
Batch processing βœ… validateEmailBatch (max 100 / call)
Built-in caching βœ… EdgeCache (in-memory) + Cloudflare KV

ΒΉ See the DNS resolver example above. Bring your own resolver β€” the serverless build doesn't bundle one.

For full docs (DNS resolver patterns, KV write-through, Durable Objects, Deno Deploy, bundle-size table, migration diff), see SERVERLESS.md.

πŸ”¬ Verification Transcript

Set captureTranscript: true on verifyEmail to get a structured per-step trace of everything the library did β€” what was looked up, what came back, how long each step took, and (for SMTP) the full wire-level transcript:

import { verifyEmail } from '@emailcheck/email-validator-js';

const result = await verifyEmail({
  emailAddress: '[email protected]',
  verifyMx: true,
  verifySmtp: true,
  checkDisposable: true,
  checkFree: true,
  detectName: true,
  suggestDomain: true,
  captureTranscript: true,
});

for (const step of result.transcript ?? []) {
  console.log(`[${step.kind}] ${step.durationMs}ms ok=${step.ok}`, step.details);
}

Each entry has:

interface VerificationStep {
  kind:
    | 'syntax' | 'domain-validation' | 'name-detection' | 'domain-suggestion'
    | 'disposable' | 'free' | 'mx-lookup' | 'smtp-probe'
    | 'whois-age' | 'whois-registration';
  startedAt: number;       // Date.now() at step start
  durationMs: number;
  ok: boolean;             // false if the step threw
  details: Record<string, unknown>;  // step-specific structured data
}

Step-specific details shapes:

kind Notable details fields
mx-lookup domain, records, count
smtp-probe port, verdict ('deliverable' | 'undeliverable' | 'indeterminate'), cacheHit, transcript, commands
whois-age creationDate, ageInDays, ageInYears
whois-registration isRegistered, isExpired, isLocked, isPendingDelete, daysUntilExpiration, status[]
disposable / free domain, isDisposable / isFree
name-detection detected ({ firstName, lastName, confidence } or null)
domain-suggestion suggestion ({ original, suggested, confidence } or null)

When captureTranscript is not set (the default), no recording happens and result.transcript is undefined β€” zero overhead.

Classifying a flattened error string

If you have a stringified SMTP error in hand (e.g. from a logged bounce, or result.smtp.error), use parseSmtpError to get a structured verdict:

import { parseSmtpError } from '@emailcheck/email-validator-js';

const parsed = parseSmtpError('552 5.2.2 mailbox over quota');
// { isDisabled: false, hasFullInbox: true, isCatchAll: false, isInvalid: false }

The four flags are orthogonal β€” a single message can fire multiple. See __tests__/0112-smtp-error-parser.test.ts for the full classification matrix.

Refining a coarse reason via the RFC 3463 enhanced status

verifyMailboxSMTP returns a coarse error reason (not_found / over_quota / temporary_failure / ambiguous / …) that's stable across MX implementations. When the MX includes an enhanced status code (RFC 3463) β€” e.g. 5.1.1 for "user unknown" vs. 5.7.1 for "policy block" β€” pipe both through refineReasonByEnhancedStatus to get a more specific reason:

import { refineReasonByEnhancedStatus, verifyMailboxSMTP } from '@emailcheck/email-validator-js';

const { smtpResult } = await verifyMailboxSMTP({
  local: 'alice', domain: 'example.com', mxRecords: ['mx.example.com'],
});
const refined = refineReasonByEnhancedStatus(smtpResult.error, smtpResult.enhancedStatus);
// e.g. 'mailbox_does_not_exist' instead of 'not_found' when the MX returned 550 5.1.1

Mapping (codes not in the table return the original reason unchanged):

DSN code Refined reason
5.1.1 mailbox_does_not_exist
5.1.2 bad_destination_system
5.1.3 bad_destination_address
5.1.6 mailbox_moved
5.1.10 recipient_address_has_null_mx
5.2.0 mailbox_status_other
5.2.1 mailbox_disabled
5.2.2 mailbox_full
5.2.3 message_too_long
5.2.4 mailing_list_expansion_problem
4.4.1 no_answer_from_host
4.4.2 bad_connection
5.7.0 security_other
5.7.1 delivery_not_authorized
5.7.25 no_reverse_dns
5.7.26 multiple_authentication_failures

πŸ“Š Performance & Caching

The library includes intelligent caching to improve performance:

Cache Type TTL Description
MX Records 1 hour DNS MX record lookups
Disposable 24 hours Disposable email checks
Free Provider 24 hours Free email provider checks
Domain Valid 24 hours Domain validation results
SMTP 30 minutes SMTP verification results
Domain Suggestions 24 hours Domain typo suggestions

Performance Tips

  1. Use Batch Processing: For multiple emails, use verifyEmailBatch() for parallel processing
  2. Enable Caching: Caching is automatic and reduces repeated lookups by ~90%
  3. Adjust Timeouts: Lower timeouts for faster responses, higher for accuracy
  4. Skip SMTP: If you only need format/MX validation, skip SMTP for 10x faster results
  5. Domain Suggestions: Cached for 24 hours to avoid recalculating similarity scores
  6. Name Detection: Lightweight operation with minimal performance impact

πŸ—‚οΈ Email Provider Databases

Disposable Email Providers (βœ… Always Updated)

View List - 5,000+ disposable email domains

Free Email Providers (βœ… Always Updated)

View List - 1,000+ free email providers

Common Email Domains (βœ… NEW)

Access the list of 70+ common email domains used for typo detection:

import { COMMON_EMAIL_DOMAINS } from '@emailcheck/email-validator-js';

console.log(COMMON_EMAIL_DOMAINS);
// ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', ...]

// Use with your own domain validation
const isCommon = COMMON_EMAIL_DOMAINS.includes('gmail.com'); // true

Testing

Default suite (fast, deterministic β€” no network):

bun run test

Real-network integration suite (INTEGRATION=1 is set automatically):

bun run test:integration

Everything:

bun run test:all

Lint:

bun run lint        # check
bun run lint:fix    # auto-fix

Typecheck + build:

bun run typecheck
bun run build

Code Quality & Maintenance

Quality Assurance

  • βœ… TypeScript Strict Mode: Full type safety with comprehensive type checking
  • βœ… Biome: Automated lint + format
  • βœ… bun:test: 720+ unit & mocked-IO tests, 0 jest, 0 sinon
  • βœ… CI/CD: Automated test + lint + build on all PRs

Project Structure

email-validator-js/
β”œβ”€β”€ src/                          # Library sources
β”‚   β”œβ”€β”€ index.ts                 # Public entry β€” verifyEmail orchestrator
β”‚   β”œβ”€β”€ email-validator.ts       # Format / TLD validation
β”‚   β”œβ”€β”€ smtp-verifier.ts         # SMTP probe (class-based state machine)
β”‚   β”œβ”€β”€ smtp-error-parser.ts     # parseSmtpError public utility
β”‚   β”œβ”€β”€ transcript.ts            # Transcript collector for verifyEmail
β”‚   β”œβ”€β”€ mx-resolver.ts           # DNS MX lookup with cache
β”‚   β”œβ”€β”€ whois.ts                 # WHOIS query pipeline
β”‚   β”œβ”€β”€ whois-parser.ts          # TLD-specific WHOIS parsers
β”‚   β”œβ”€β”€ domain-suggester.ts      # Typo / similarity suggestions
β”‚   β”œβ”€β”€ name-detector.ts         # Local-part name extraction
β”‚   β”œβ”€β”€ is-spam-email.ts         # Spam-pattern detection
β”‚   β”œβ”€β”€ batch-verifier.ts        # Concurrent batch validator
β”‚   β”œβ”€β”€ cache.ts / cache-interface.ts  # Pluggable cache surface
β”‚   β”œβ”€β”€ adapters/
β”‚   β”‚   β”œβ”€β”€ lru-adapter.ts       # In-memory LRU
β”‚   β”‚   └── redis-adapter.ts     # Redis-backed (SCAN-safe clear)
β”‚   β”œβ”€β”€ data/                    # Source-of-truth JSON tables
β”‚   β”‚   β”œβ”€β”€ common-{first,last}-names.json
β”‚   β”‚   β”œβ”€β”€ common-email-domains.json
β”‚   β”‚   β”œβ”€β”€ typo-patterns.json
β”‚   β”‚   └── whois-servers.json
β”‚   β”œβ”€β”€ types.ts                 # Public type definitions
β”‚   └── serverless/              # Edge-runtime variant
β”‚       β”œβ”€β”€ verifier.ts          # No-Node-deps validator
β”‚       β”œβ”€β”€ _shared/             # Cross-platform helpers
β”‚       β”‚   β”œβ”€β”€ cors.ts
β”‚       β”‚   β”œβ”€β”€ dispatch.ts
β”‚       β”‚   └── validation.ts
β”‚       └── adapters/            # AWS, GCP, Vercel, Cloudflare, Netlify, Azure
β”œβ”€β”€ __tests__/
β”‚   β”œβ”€β”€ unit/                    # Default suite β€” pure unit + mocked-IO
β”‚   β”œβ”€β”€ isolated/                # Tests using mock.module (own bun-test process)
β”‚   β”œβ”€β”€ integration/             # Real-network suite (INTEGRATION=1)
β”‚   β”œβ”€β”€ helpers/                 # Shared fake-net + setup
β”‚   └── utils/                   # Shared test fixtures
β”œβ”€β”€ extras/check-if-email-exists/  # Out-of-scope module + its tests
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ smtp/                    # Direct SMTP-probe API usage
β”‚   β”œβ”€β”€ cache/                   # Custom CacheStore implementations
β”‚   β”œβ”€β”€ high-level/              # verifyEmail orchestration / names / domains
β”‚   β”œβ”€β”€ integrations/            # Patterns for plugging into other tools
β”‚   └── serverless/              # One folder per platform (AWS, GCP, ...)
└── dist/                        # Rollup output (CJS + ESM)

Scripts

bun run build              # Rollup CJS + ESM bundles
bun run test               # Default: unit + isolated (~770 tests)
bun run test:unit          # Just the unit suite
bun run test:isolated      # Tests needing mock.module isolation
bun run test:integration   # Real-network suite (INTEGRATION=1)
bun run test:extras        # check-if-email-exists module (opt-in, ~200 tests)
bun run test:all           # test + test:integration
# Domain-specific filters (glob over __tests__/unit/):
bun run test:smtp          # 01xx
bun run test:cache         # 02xx
bun run test:whois         # 03xx
bun run test:names         # 04xx
bun run test:serverless    # 05xx + isolated/
bun run test:cli           # 07xx
bun run lint               # Biome check
bun run lint:fix           # Biome check --write
bun run typecheck          # tsc against src + tests + examples

Contributing

We welcome contributions! Please feel free to open an issue or create a pull request.

Development Setup

# Clone
git clone https://github.com/email-check-app/email-validator-js.git
cd email-validator-js

# Install with Bun
bun install

# Run the default (no-network) suite
bun run test

# Build
bun run build

Support

For issues, questions, or commercial licensing:

πŸ› Open an Issue πŸ“§ Email Support πŸ“„ Commercial License 🌐 Visit email-check.app

LICENSE

Business Source License 1.1 - see LICENSE file for details.

πŸ“ When Do You Need a Commercial License?

The BSL allows use only for non-production purposes. Here's a comprehensive guide to help you understand when you need a commercial license:

Use Case Commercial License Required? Details
Personal & Learning
πŸ”¬ Exploring email-validator-js for research or learning βœ… No Use freely for educational purposes
🎨 Personal hobby projects (non-commercial) βœ… No Build personal tools and experiments
πŸ§ͺ Testing and evaluation in development environment βœ… No Test all features before purchasing
Development & Prototyping
πŸ’‘ Building proof-of-concept applications βœ… No Create demos and prototypes
πŸ› οΈ Internal tools (not customer-facing) βœ… No Use for internal development tools
πŸ“š Open source projects (non-commercial) βœ… No Contribute to the community
Commercial & Production Use
πŸ’° Revenue-generating applications ❌ Yes Any app that generates income
☁️ Software as a Service (SaaS) products ❌ Yes Cloud-based service offerings
πŸ“¦ Distributed commercial software ❌ Yes Software sold to customers
🏒 Enterprise production systems ❌ Yes Business-critical applications
πŸ”„ Forking for commercial purposes ❌ Yes Creating derivative commercial products
🏭 Production use in any form ❌ Yes Live systems serving real users
Specific Scenarios
πŸŽ“ Student projects and coursework βœ… No Academic use is encouraged
πŸ—οΈ CI/CD pipelines (for commercial products) ❌ Yes Part of commercial development
πŸ“§ Email validation in production APIs ❌ Yes Production service usage
πŸ›’ E-commerce checkout validation ❌ Yes Revenue-related validation
πŸ“± Mobile apps (free with ads or paid) ❌ Yes Monetized applications

πŸ’‘ Quick Decision Guide

Ask yourself these questions:

  1. Will real users interact with this in production? β†’ You need a license
  2. Will this help generate revenue? β†’ You need a license
  3. Is this for learning or testing only? β†’ No license needed
  4. Is this an internal prototype or POC? β†’ No license needed

🎯 Why Choose Our Commercial License?

✨ Unlimited Usage - Use in all your production applications
πŸš€ Priority Support - Direct support from our engineering team
πŸ”„ Regular Updates - Get the latest features and improvements
πŸ›‘οΈ Legal Protection - Full commercial rights and warranty
🏒 Enterprise Ready - Suitable for large-scale deployments

πŸ“„ Get Your Commercial License

Ready to use email-validator-js in production?

πŸ›οΈ Purchase a License - Simple pricing, instant activation
πŸ“§ Contact Sales - For enterprise or custom needs

About

Verify email address checking MX records, and SMTP connection, check for disposable email addresses and free email providers.

Topics

Resources

License

Stars

9 stars

Watchers

0 watching

Forks

Contributors