Skip to main content

Security Toolbox

Verify Email

Check whether an email address looks deliverable. The tool checks syntax, then DNS (MX, with an A/AAAA fallback), and can optionally probe SMTP up to RCPT TO. It never sends a message.

Warning: Authorised use only. SMTP probing talks to someone else's mail server. Use it only on addresses and domains you are allowed to test.

Install

Python 3.10 or newer. dnspython installs with the package.

pip install lupaxa-verify-email
verify-email --help

CLI

verify-email user@example.com
verify-email user@example.com --inspection 4 --from-address probe@yourdomain.example
verify-email --file emails.txt --inspection 3 --from-address probe@yourdomain.example
verify-email user@example.com --format json --output results.json
python -m lupaxa.verify_email --version

emails.txt is one address per line. Blank lines and lines starting with # are skipped. A leading UTF-8 BOM is ignored. A file with no addresses exits 2. During SMTP checks the tool waits 1 second between addresses in a file. --delay 0 turns that pause off. --delay is 0 for syntax and DNS checks.

An MX record names a host and has no port. Inbound delivery uses port 25. Pass --port when the server you want to probe is listening somewhere else. Port 465 is implicit TLS. Any other port starts in cleartext and upgrades when the server offers STARTTLS. The probe does not verify the certificate. If the handshake fails, that host is skipped.

Unicode domains are looked up as A-labels. A Unicode local part is sent with SMTPUTF8 when the server advertises it. If the server does not, the result is unknown.

--from-address is required for inspection levels 3 and 4. There is no default sender. RCPT TO is only trusted after MAIL FROM returns 250. A 4xx on MAIL FROM is treated like a 4xx on RCPT TO. Pass an address or --file, not both. Inspection must be 1, 2, 3, or 4. --debug writes the DNS and SMTP dialogue to stderr, so JSON on stdout stays intact.

CLI Flags

Flag Default Description
--file — File of addresses, one per line
--inspection 2 Depth: 1 syntax, 2 DNS, 3 SMTP lenient, 4 strict
--from-address required for 3–4 MAIL FROM address. Required when SMTP runs
--timeout 5 DNS and SMTP timeout in seconds
--port 25 SMTP port. MX records do not carry one
--format text text blocks or a JSON list
--output stdout Write the chosen format to this file
--delay 1 or 0 Seconds between file addresses. 1 when SMTP runs
--debug off Print DNS and SMTP dialogue to stderr
--version — Print the package version and exit

Exit Codes

Code Single address File
0 valid Every address is valid
1 invalid, or no address was supplied At least one address is invalid
2 unknown, or bad usage No invalids, but at least one unknown

Exit 1 is also used when the address file cannot be read, including a file that is not UTF-8. Exit 2 is also used when the file has no addresses, --from-address is missing for an SMTP check, the inspection level is outside 1–4, an address and --file are both set, --delay is negative, or the port or timeout is unusable.

Inspection Levels

Level What it checks valid means
1 Address shape, local-part, and domain It looks like an email
2 Level 1, plus MX (or A/AAAA when there is no MX) The domain can receive mail
3 Level 2, plus an SMTP probe RCPT 250, 251, 252, or 552; 4xx counts as yes
4 Same probe as level 3 Same, but 4xx replies are unknown

Every run returns valid, invalid, or unknown. A null MX (0 .) or NXDOMAIN is invalid. A DNS timeout or SERVFAIL is unknown, so a resolver outage is not reported as a bad address.

252 means the server will accept the message without confirming the mailbox. Catch-all domains often answer that way, so levels 3 and 4 can report them as valid.

SMTP replies

The probe reads each reply until the final line, a status code followed by a space rather than a hyphen, and that line has arrived in full. It tries the next MX after a connection failure, a failed TLS handshake, a MAIL FROM rejection, or an unknown RCPT reply.

Reply Result
250, 251, 252 valid
552, or enhanced status 5.2.2 valid. The mailbox is full and cannot accept emails now
421, 450, 451, 452, 454, 455 valid at level 3, unknown at level 4
Other 4.x.x enhanced status Same as the 4xx row
550, 551, 553 for a missing mailbox invalid. Includes 5.1.1 and “user unknown”
550 or 554 with 5.7.x, relay, policy, or a block unknown
554 with no mailbox detail unknown
500–504, 521, 530, 535 unknown

5.7.x is a policy or security refusal. It says the server would not complete the probe, which is different from a missing mailbox. The mailbox echoed in the reply is ignored, so spam-trap@example.com is not treated as a spam or block reply. A full mailbox stays valid at both SMTP levels, and result.ok is true, with the reason mailbox is full and cannot accept emails at this time.

Library

from lupaxa.verify_email import EmailValidator

validator = EmailValidator(
    inspection=3,
    from_address="probe@yourdomain.example",
)
result = validator.validate("user@example.com")
print(result.status, result.stage, result.reason)

validate always returns a ValidationResult. It does not raise for a bad address.

Field Type Meaning
status str valid, invalid, or unknown
stage str basic, localpart, domain, syntax, dns, smtp
reason str Why that status was chosen
inspection int Level used (1–4)
address str Address that was checked
from_address str MAIL FROM used for SMTP, or empty
timeout int Timeout in seconds
port int SMTP port that was probed
mx_hosts list[str] MX hosts tried, if DNS ran

Some providers refuse mailbox probes. Those come back as unknown. A catch-all domain can look valid at levels 3 and 4. A Unicode local part is accepted at syntax and is probed only when the server offers SMTPUTF8.

Development

make init
make python-install-dev
make python-check
The Lupaxa Project Footer

Metadata

Release files for lupaxa-verify-email 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lupaxa-verify-email 0.1.0
File Size Uploaded
lupaxa_verify_email-0.1.0.tar.gz 16.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lupaxa-verify-email 0.1.0
File Interpreter ABI Platform
lupaxa_verify_email-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.9 kB

Release files / lupaxa_verify_email-0.1.0.tar.gz

Download URL lupaxa_verify_email-0.1.0.tar.gz
Size 16.4 kB
Tags Source
SHA-256 checksum
How to use checksums
83f56b83941197f4784717e50746307beed7ee983d5c7a9b473b66f805c31954
BLAKE2b-256 checksum
How to use checksums
bbe95e22e2695f23a2d4c691cffea101a29e92cb3660c7cef2d20a1b2a931f79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / lupaxa_verify_email-0.1.0-py3-none-any.whl

Download URL lupaxa_verify_email-0.1.0-py3-none-any.whl
Size 19.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4836fa5ab99c933c48f9be9890e53782fb889ed057442cc2678b1453b64e858c
BLAKE2b-256 checksum
How to use checksums
c3562da5337b8465a3b2c7b5fa7316fe700293b5bdf234af340292cb06650056
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page