bouncevalidator
v0.0.2
Published
A completely local email validation service - alternative to ZeroBounce, Bouncify, and Debounce
Readme
BounceValidator
A completely local email validation service for Node.js. An alternative to cloud-based services like ZeroBounce, Bouncify, and Debounce - with zero external API dependencies.
Features
- 100% Local - No external API calls, all validation happens on your machine
- Syntax Validation - RFC 5322 compliant email format validation
- DNS/MX Verification - Verifies domain has valid mail exchange records
- SMTP Verification - Connects to mail servers to verify mailbox existence
- Disposable Email Detection - Identifies temporary/throwaway email addresses
- Role Account Detection - Flags role-based emails (admin@, support@, etc.)
- Free Provider Detection - Identifies emails from free providers (Gmail, Yahoo, etc.)
- Typo Suggestions - Suggests corrections for common domain typos
- Catch-All Detection - Identifies domains that accept all addresses
- Bulk Validation - Process multiple emails with configurable concurrency
- DNS Caching - Improves performance for repeated lookups
- CLI & Programmatic API - Use from command line or as a library
Installation
npm install bouncevalidatorOr with yarn:
yarn add bouncevalidatorQuick Start
Programmatic Usage
import { validate } from 'bouncevalidator';
const result = await validate('[email protected]');
console.log(result.isReachable); // 'safe', 'invalid', 'risky', or 'unknown'
console.log(result.syntax.isValidSyntax); // true/false
console.log(result.mx.acceptsMail); // true/false
console.log(result.smtp.isDeliverable); // true/false
console.log(result.misc.isDisposable); // true/falseCLI Usage
# Validate a single email
npx bouncevalidator [email protected]
# Validate with pretty output
npx bouncevalidator -p [email protected]
# Quick validation (skip SMTP)
npx bouncevalidator -q [email protected]
# Validate from file
npx bouncevalidator -f emails.txt -o results.jsonAPI Reference
validate(email, options?)
Validates a single email address through the full verification pipeline.
Parameters:
email(string) - The email address to validateoptions(ValidationOptions) - Optional configuration
Returns: Promise<ValidationResult>
import { validate } from 'bouncevalidator';
const result = await validate('[email protected]', {
smtpTimeout: 15000,
checkGravatar: true
});validateBulk(emails, options?)
Validates multiple email addresses with configurable concurrency.
Parameters:
emails(string[]) - Array of email addressesoptions(BulkValidationOptions) - Optional configuration
Returns: Promise<ValidationResult[]>
import { validateBulk } from 'bouncevalidator';
const results = await validateBulk([
'[email protected]',
'[email protected]',
'[email protected]'
], {
concurrency: 10,
onProgress: (completed, total, result) => {
console.log(`${completed}/${total}: ${result.input} - ${result.isReachable}`);
}
});validateQuick(email)
Quick validation that only checks syntax and MX records (skips SMTP verification).
Parameters:
email(string) - The email address to validate
Returns: Promise<ValidationResult>
import { validateQuick } from 'bouncevalidator';
const result = await validateQuick('[email protected]');Individual Validators
You can use individual validators for more control:
import {
validateSyntax,
verifyMx,
verifySmtp,
checkMisc,
checkDisposable,
checkRoleAccount,
checkFreeProvider
} from 'bouncevalidator';
// Syntax validation only
const syntax = validateSyntax('[email protected]');
// MX verification only
const mx = await verifyMx('example.com');
// Check if domain is disposable
const isDisposable = checkDisposable('tempmail.com');
// Check if email is role-based
const isRole = checkRoleAccount('admin');Utility Functions
import {
normalizeEmail,
extractParts,
getSuggestion,
getEmailSuggestion
} from 'bouncevalidator';
// Normalize email
const normalized = normalizeEmail('[email protected]'); // '[email protected]'
// Extract parts
const parts = extractParts('[email protected]');
// { username: 'user', domain: 'example.com' }
// Get domain typo suggestion
const suggestion = getSuggestion('gmial.com'); // 'gmail.com'
// Get full email suggestion
const emailSuggestion = getEmailSuggestion('[email protected]');
// '[email protected]'Configuration Options
ValidationOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| smtpTimeout | number | 10000 | SMTP connection timeout in milliseconds |
| verifySmtp | boolean | true | Whether to perform SMTP verification |
| checkDisposable | boolean | true | Check for disposable email domains |
| checkRoleAccount | boolean | true | Check for role-based accounts |
| checkFreeProvider | boolean | true | Check for free email providers |
| checkGravatar | boolean | false | Check for Gravatar existence |
| heloHost | string | hostname | Custom HELO/EHLO hostname |
| senderAddress | string | '' | Custom MAIL FROM address |
| dnsCacheTtl | number | 300000 | DNS cache TTL in milliseconds |
| useDnsCache | boolean | true | Whether to use DNS caching |
| detectCatchAll | boolean | true | Detect catch-all domains |
BulkValidationOptions
Extends ValidationOptions with:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| concurrency | number | 5 | Maximum concurrent validations |
| delayBetween | number | 0 | Delay between batches in milliseconds |
| onProgress | function | - | Progress callback function |
ValidationResult Structure
interface ValidationResult {
input: string; // Original email input
isReachable: Reachability; // 'safe' | 'invalid' | 'risky' | 'unknown'
syntax: {
address: string; // Original address
domain: string; // Domain part
username: string; // Local part
isValidSyntax: boolean; // Syntax validity
normalizedEmail: string; // Normalized version
suggestion: string | null; // Typo suggestion
};
mx: {
acceptsMail: boolean; // Has valid MX records
records: string[]; // List of MX hostnames
};
smtp: {
canConnectSmtp: boolean; // Could connect to server
isDeliverable: boolean; // Mailbox exists
isCatchAll: boolean; // Domain is catch-all
error: {
type: string; // Error type
message: string; // Error message
} | null;
};
misc: {
isDisposable: boolean; // Disposable domain
isRoleAccount: boolean; // Role-based account
isFreeProvider: boolean; // Free email provider
gravatarUrl: string | null; // Gravatar URL if exists
};
}Reachability Status
| Status | Description |
|--------|-------------|
| safe | Email is verified to exist and can receive mail |
| invalid | Email has invalid syntax, domain doesn't exist, or mailbox not found |
| risky | Email might be valid but couldn't be verified (catch-all, greylisted, etc.) |
| unknown | Verification was inconclusive due to server issues |
CLI Reference
bouncevalidator - Local email validation service
USAGE:
bouncevalidator [OPTIONS] <email>...
bouncevalidator -f <file> [OPTIONS]
ARGUMENTS:
<email>... Email addresses to validate
OPTIONS:
-h, --help Show help message
-v, --version Show version number
-f, --file <path> Read emails from file (one per line)
-o, --output <path> Write results to file
-q, --quick Quick mode (skip SMTP verification)
-p, --pretty Pretty print JSON output
--verbose Show detailed progress
VALIDATION OPTIONS:
-t, --timeout <ms> SMTP timeout (default: 10000)
-c, --concurrency <n> Max concurrent validations (default: 5)
--no-smtp Skip SMTP verification
--no-disposable Skip disposable email check
--no-role Skip role account check
--no-free-provider Skip free provider check
--gravatar Check for Gravatar
EXIT CODES:
0 All emails are valid (safe)
1 One or more emails are invalid
2 One or more emails are risky or unknown
3 Error occurred during validationExamples
Basic Validation
import { validate } from 'bouncevalidator';
async function checkEmail(email: string) {
const result = await validate(email);
if (result.isReachable === 'safe') {
console.log('Email is valid and deliverable');
} else if (result.isReachable === 'invalid') {
console.log('Email is invalid:', result.smtp.error?.message);
} else {
console.log('Email status uncertain:', result.isReachable);
}
}Bulk Validation with Progress
import { validateBulk } from 'bouncevalidator';
import fs from 'fs';
async function validateEmailList(filePath: string) {
const emails = fs.readFileSync(filePath, 'utf-8')
.split('\n')
.map(e => e.trim())
.filter(e => e.length > 0);
const results = await validateBulk(emails, {
concurrency: 10,
onProgress: (completed, total, result) => {
const pct = Math.round((completed / total) * 100);
process.stdout.write(`\r[${pct}%] Validating emails...`);
}
});
const valid = results.filter(r => r.isReachable === 'safe');
const invalid = results.filter(r => r.isReachable === 'invalid');
console.log(`\nValid: ${valid.length}, Invalid: ${invalid.length}`);
return results;
}Custom Validation Pipeline
import {
validateSyntax,
verifyMx,
checkDisposable,
checkRoleAccount
} from 'bouncevalidator';
async function customValidation(email: string) {
// Step 1: Check syntax
const syntax = validateSyntax(email);
if (!syntax.isValidSyntax) {
return { valid: false, reason: 'Invalid syntax' };
}
// Step 2: Check if disposable
if (checkDisposable(syntax.domain)) {
return { valid: false, reason: 'Disposable email' };
}
// Step 3: Check if role account
if (checkRoleAccount(syntax.username)) {
return { valid: false, reason: 'Role-based account' };
}
// Step 4: Verify MX records
const mx = await verifyMx(syntax.domain);
if (!mx.acceptsMail) {
return { valid: false, reason: 'Domain does not accept mail' };
}
return { valid: true, domain: syntax.domain, mx: mx.records };
}Express.js Integration
import express from 'express';
import { validate, validateBulk } from 'bouncevalidator';
const app = express();
app.use(express.json());
// Single email validation endpoint
app.post('/api/validate', async (req, res) => {
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email is required' });
}
try {
const result = await validate(email);
res.json(result);
} catch (err) {
res.status(500).json({ error: 'Validation failed' });
}
});
// Bulk validation endpoint
app.post('/api/validate/bulk', async (req, res) => {
const { emails } = req.body;
if (!Array.isArray(emails)) {
return res.status(400).json({ error: 'Emails array is required' });
}
try {
const results = await validateBulk(emails, { concurrency: 5 });
res.json(results);
} catch (err) {
res.status(500).json({ error: 'Validation failed' });
}
});
app.listen(3000);How It Works
1. Syntax Validation
The first step validates the email format against RFC 5322 standards:
- Checks for valid characters in local and domain parts
- Validates domain structure and TLD
- Enforces length limits (254 chars total, 64 chars local part)
2. DNS/MX Verification
Next, we query DNS to verify the domain can receive email:
- Looks up MX (Mail Exchange) records
- Falls back to A/AAAA records if no MX exists (per RFC 5321)
- Detects null MX records (RFC 7505)
- Caches results for performance
3. SMTP Verification
The most reliable check - we connect to the mail server:
- Connect to the highest priority MX host
- Send EHLO/HELO greeting
- Initiate MAIL FROM command
- Send RCPT TO with the target email
- Interpret the server's response
Response codes:
- 250: Mailbox exists
- 550: Mailbox not found
- 450/451: Greylisted or temporary failure
- 552: Mailbox disabled
4. Additional Checks
Finally, we perform supplementary checks:
- Disposable detection: Compares domain against 500+ known disposable email services
- Role account detection: Checks for common role-based prefixes (admin, support, info, etc.)
- Free provider detection: Identifies free email services (Gmail, Yahoo, Outlook, etc.)
- Catch-all detection: Tests if domain accepts any random address
Limitations
- SMTP verification may be blocked: Some mail servers block or rate-limit SMTP verification attempts
- Greylisting: Some servers use greylisting which causes temporary rejections
- Catch-all domains: Cannot definitively verify addresses on catch-all domains
- Network dependent: Requires network access for DNS and SMTP operations
- No DMARC/SPF checks: Does not validate sending reputation
Performance Tips
- Use DNS caching: Enabled by default, reduces repeated DNS lookups
- Adjust concurrency: Higher concurrency for bulk operations, but be mindful of rate limits
- Use quick mode: Skip SMTP verification when you only need syntax/MX validation
- Batch operations: Use
validateBulkinstead of multiplevalidatecalls
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
Author
Developed and maintained by HyScaler Node.js Team.
HyScaler is a software development company specializing in building scalable, high-performance applications. We help businesses scale their technology with expert development services.
License
MIT
