Official VerifyAnyEmail SDK for Python — real-time and bulk email verification.
Project description
VerifyAnyEmail — Python SDK
Official Python SDK for the VerifyAnyEmail API — real-time and bulk email verification.
- Zero third-party runtime dependencies — stdlib only (
urllib,json,hmac,hashlib). - Python 3.8+.
- Typed results (
VerificationResult,Checks) with the raw JSON always available on.raw. - Built-in webhook signature verification (constant-time).
Server-side use only. Your API key can spend credits and read your account data — keep it secret. Never embed it in browser/mobile/client code. Load it from an environment variable or secrets manager.
Install
pip install verifyanyemail
Quickstart
Verify a single address
from verifyanyemail import VerifyAnyEmail
client = VerifyAnyEmail(api_key="vae_...")
result = client.verify("name@example.com")
print(result.status) # deliverable | undeliverable | risky | unknown
print(result.score) # 0..100
print(result.safe_to_send) # bool
print(result.suggestion) # "did you mean" correction, or None
print(result.checks.mailbox_exists)
# The full JSON payload is always available:
print(result.raw)
Options (both default to the server default of True):
# DNS-only (skip the live SMTP probe), and skip catch-all detection:
result = client.verify(
"name@example.com",
smtp_probe=False,
catch_all_detection=False,
)
Test mode (free)
Any address at @test.verifyany.email returns a deterministic result
without charging a credit or probing a real mailbox:
client.verify("deliverable@test.verifyany.email") # -> deliverable
client.verify("undeliverable@test.verifyany.email") # -> undeliverable
client.verify("risky@test.verifyany.email") # -> risky
client.verify("unknown@test.verifyany.email") # -> unknown
Batch: submit and poll
import time
from verifyanyemail import VerifyAnyEmail
client = VerifyAnyEmail(api_key="vae_...")
submitted = client.verify_batch(
emails=["a@example.com", "b@example.com"],
name="newsletter-2026-07",
webhook_url="https://your-app.example.com/webhooks/vae", # optional
)
batch_id = submitted["batch"]["id"]
# Poll until finished
while True:
status = client.get_batch(batch_id)["batch"]
if status["status"] in ("completed", "failed"):
break
time.sleep(5)
# Page through results
page = 1
while True:
resp = client.get_batch_results(batch_id, page=page, page_size=100)
for row in resp["results"]:
print(row["email"], row["status"], row["score"])
if page * resp["pageSize"] >= resp["total"]:
break
page += 1
Verify a webhook
When a batch completes, VerifyAnyEmail POSTs a signed batch.completed
event to your webhookUrl with the header:
X-VAE-Signature: sha256=<HMAC-SHA256(rawBody, yourWebhookSecret)>
Verify it against the raw request body (do not re-serialize the JSON):
from verifyanyemail import verify_webhook_signature
# e.g. inside a Flask view
raw_body = request.get_data() # bytes
signature = request.headers.get("X-VAE-Signature", "")
secret = "whsec_..." # dashboard Settings
if not verify_webhook_signature(raw_body, signature, secret):
abort(400) # reject
# ...process the event, return 2xx to acknowledge
It is also available as VerifyAnyEmail.verify_webhook_signature(...).
Method reference
VerifyAnyEmail(api_key, base_url="https://api.verifyany.email", timeout=30.0)
| Method | Returns | Notes |
|---|---|---|
verify(email, smtp_probe=None, catch_all_detection=None) |
VerificationResult |
Single address; unwraps {ok, result}. |
verify_batch(emails, name=None, webhook_url=None) |
dict |
{"ok", "batch": {"id", "status", "total"}}. |
get_batch(batch_id) |
dict |
{"ok", "batch": {...}} with status + summary. |
get_batch_results(batch_id, page=1, page_size=100) |
dict |
{"ok", "page", "pageSize", "total", "results": [...]}. |
verify_webhook_signature(raw_body, signature_header, secret) |
bool |
Static/module-level; constant-time. |
VerificationResult fields
email, normalized, account, domain, status, sub_status,
score, checks (a Checks), suggestion, safe_to_send,
duration_ms, checked_at, and raw (the full JSON dict).
Checks fields
syntax, has_mx_records, null_mx, smtp_connectable,
mailbox_exists, catch_all, role_based, disposable,
free_provider, possible_typo, gibberish, and raw. Fields the API
marks nullable may be None (e.g. when the SMTP probe was skipped).
Error handling
Any non-2xx response raises VerifyAnyEmailError:
from verifyanyemail import VerifyAnyEmail, VerifyAnyEmailError
client = VerifyAnyEmail(api_key="vae_...")
try:
result = client.verify("name@example.com")
except VerifyAnyEmailError as exc:
print(exc.status) # HTTP status code
print(exc.code) # machine-readable code (JSON "error")
print(exc.message) # human-readable message
print(exc.body) # raw parsed JSON body (or None)
Common statuses:
| Status | Meaning |
|---|---|
400 |
Invalid input |
401 |
Missing or invalid API key |
402 |
Insufficient credits |
403 |
Feature not on your plan (batch) |
413 |
Batch too large for your plan |
429 |
Rate limited / concurrent batch limit reached |
Network/timeout failures raise VerifyAnyEmailError with status == 0
and code == "network_error".
License
MIT © 2026 VerifyAnyEmail
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file verifyanyemail-1.0.0.tar.gz.
File metadata
- Download URL: verifyanyemail-1.0.0.tar.gz
- Upload date:
- Size: 8.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2435c2dfe19f48f6bdc6be16298ba5be986533fe236e1aa611ce30336c004fa
|
|
| MD5 |
4d4bc1fd464d470df1c2e4b355165a8e
|
|
| BLAKE2b-256 |
67194e7ab325b7fbeacfbabe8e41e32ede58a8c1df6ffe1df1e3870a1774df18
|
File details
Details for the file verifyanyemail-1.0.0-py3-none-any.whl.
File metadata
- Download URL: verifyanyemail-1.0.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dcaa7dd9dbb12bc15f45938c0f17e8818c59aab44538ad1f5fb159d7abdf14e
|
|
| MD5 |
9cff0162f21c977e46ff906fda0f6b95
|
|
| BLAKE2b-256 |
d418f0f9381cafbf7f1ffbd71884f10b2f7218a2c1fed0ca1613e58efcc6f855
|