email-verification — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited email-verification (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Verify and clean email lists using multi-level checks: syntax validation, DNS/MX lookup, disposable domain detection, role-based address flagging, and typo domain correction. Ensures only deliverable addresses reach your campaign tools.
python3 email_verifier.py --email [email protected]python3 email_verifier.py \
--input leads.csv \
--email-column email \
--output leads_verified.csv \
--workers 20python3 email_verifier.py \
--input leads.csv \
--email-column email \
--output results.csv \
--workers 20 \
--skip-smtp| Parameter | Flag | Description | Default |
|---|---|---|---|
| Input file | --input, -i | CSV file with email column | (required for batch) |
| Email column | --email-column, -c | Column name containing emails | email |
| Output file | --output, -o | Output CSV path | {input}_verified.csv |
| Single email | --email, -e | Verify one email (no CSV needed) | -- |
| Workers | --workers, -w | Max concurrent threads | 5 |
| Skip SMTP | --skip-smtp | Force DNS-only mode | auto-detected |
| Status | Meaning | Action |
|---|---|---|
| VALID | Domain has MX records, syntax OK, not disposable | Send to campaign |
| INVALID | Dead domain, no MX/A records, disposable provider, bad syntax | Remove from list |
| RISKY | Role-based address (info@, admin@) or A-record-only domain | Separate low-priority sequence or exclude |
| UNKNOWN | DNS timeout or lookup failure | Re-verify later or exclude |
flags column)| Flag | Meaning |
|---|---|
ROLE_BASED | Address is a role mailbox (info@, support@, admin@, etc.) |
FREE_PROVIDER | Gmail, Yahoo, Outlook, etc. (relevant for B2B filtering) |
NO_MX_A_ONLY | Domain has A record but no MX record -- may not accept mail |
VALID -- push to primary campaign sequenceRISKY -- push to separate low-priority sequence, or excludeINVALID -- remove entirelyUNKNOWN -- re-verify in next batch or excludeimport dns.resolver
import re
import csv
import concurrent.futures
from datetime import datetime, timezone
# Disposable domain blocklist (subset -- expand as needed)
DISPOSABLE_DOMAINS = {
'mailinator.com', 'guerrillamail.com', 'tempmail.com', 'throwaway.email',
'yopmail.com', 'sharklasers.com', 'grr.la', 'guerrillamailblock.com',
'pokemail.net', 'spam4.me', 'trashmail.com', 'dispostable.com',
# Add 70+ more as needed
}
# Role-based prefixes
ROLE_PREFIXES = {
'info', 'admin', 'support', 'sales', 'contact', 'help', 'billing',
'accounts', 'webmaster', 'postmaster', 'abuse', 'noreply', 'no-reply',
'team', 'office', 'hello', 'careers', 'jobs', 'press', 'media',
'marketing', 'security', 'privacy', 'compliance', 'legal', 'hr',
'feedback', 'inquiries', 'enquiries', 'service', 'orders',
}
# Free email providers
FREE_PROVIDERS = {
'gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', 'aol.com',
'icloud.com', 'protonmail.com', 'mail.com', 'zoho.com', 'yandex.com',
}
# Common typo corrections
TYPO_DOMAINS = {
'gmial.com': 'gmail.com', 'gmai.com': 'gmail.com', 'gamil.com': 'gmail.com',
'gmali.com': 'gmail.com', 'gmal.com': 'gmail.com', 'gnail.com': 'gmail.com',
'yahooo.com': 'yahoo.com', 'yaho.com': 'yahoo.com',
'outlok.com': 'outlook.com', 'outloo.com': 'outlook.com',
'hotmal.com': 'hotmail.com', 'hotmial.com': 'hotmail.com',
}
def verify_email(email: str) -> dict:
"""Verify a single email address."""
email = email.strip().lower()
result = {
'email': email,
'status': 'UNKNOWN',
'reason': '',
'mx_host': '',
'flags': [],
'checked_at': datetime.now(timezone.utc).isoformat(),
}
# Syntax check
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
result['status'] = 'INVALID'
result['reason'] = 'Bad syntax'
return result
local, domain = email.rsplit('@', 1)
# Typo domain check
if domain in TYPO_DOMAINS:
result['status'] = 'INVALID'
result['reason'] = f'Typo domain -- did you mean {TYPO_DOMAINS[domain]}?'
return result
# Disposable domain check
if domain in DISPOSABLE_DOMAINS:
result['status'] = 'INVALID'
result['reason'] = 'Disposable email provider'
return result
# Role-based check
if local in ROLE_PREFIXES:
result['flags'].append('ROLE_BASED')
# Free provider check
if domain in FREE_PROVIDERS:
result['flags'].append('FREE_PROVIDER')
# DNS/MX lookup
try:
mx_records = dns.resolver.resolve(domain, 'MX')
result['mx_host'] = str(mx_records[0].exchange)
result['status'] = 'VALID'
result['reason'] = 'MX record found'
if result['flags'] and 'ROLE_BASED' in result['flags']:
result['status'] = 'RISKY'
result['reason'] = 'Role-based address'
except dns.resolver.NoAnswer:
# Try A record fallback
try:
dns.resolver.resolve(domain, 'A')
result['status'] = 'RISKY'
result['reason'] = 'A record only, no MX'
result['flags'].append('NO_MX_A_ONLY')
except:
result['status'] = 'INVALID'
result['reason'] = 'No DNS records'
except dns.resolver.NXDOMAIN:
result['status'] = 'INVALID'
result['reason'] = 'Domain does not exist'
except Exception as e:
result['reason'] = f'DNS lookup failed: {str(e)}'
result['flags'] = '|'.join(result['flags'])
return result--workers 20If port 25 is blocked on your server, the tool falls back to DNS-only mode. This means:
For full SMTP verification, use an external service (e.g., ZeroBounce, NeverBounce) or run from a server with port 25 open.
| Column | Description |
|---|---|
email | Lowercased, trimmed email |
status | VALID / INVALID / RISKY / UNKNOWN |
reason | Human-readable explanation |
mx_host | MX server hostname (if found) |
flags | Pipe-separated flags (ROLE_BASED, FREE_PROVIDER, NO_MX_A_ONLY) |
checked_at | ISO 8601 UTC timestamp |
Built by PureBrain -- AI-powered marketing operations.
Want the full suite of 183+ production-tested skills? Start your trial ->
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.