Python client for emailval.net — email validation API ($0.0008/email)
Project description
emailval-client
Python client for emailval.net — email validation API.
$0.0008 per email. 10× cheaper than ZeroBounce.
Installation
pip install emailval-client
Requires Python 3.10+. The only dependency is httpx.
Quickstart
from emailval import EmailValidator
ev = EmailValidator(api_key="your_api_key")
results = ev.validate(["user@company.com", "test@mailinator.com", "bad-address"])
for r in results:
print(r.email, r.score, r.is_valid)
Output:
user@company.com 100 True
test@mailinator.com 20 False
bad-address 0 False
Get your API key at emailval.net — self-serve, no sales call.
Five-layer validation pipeline
Every email passes through five layers:
| Layer | Check | Speed |
|---|---|---|
| 1 | RFC 5321/5322 syntax | <1ms |
| 2 | MX record lookup | ~50ms |
| 3 | Disposable domain (350k+ blocked domains) | <1ms |
| 4 | Catch-all detection | ~200ms |
| 5 | SMTP mailbox verification | ~300ms |
Returns a deliverability score from 0 to 100:
| Score | Meaning |
|---|---|
100 |
Valid syntax + MX + not disposable + not catch-all + SMTP confirmed |
60 |
Valid, but provider blocks SMTP verification (Gmail, Outlook, Yahoo) |
20 |
Disposable or catch-all domain |
5 |
Valid syntax but no MX records |
0 |
Invalid syntax |
result.is_valid returns True for score ≥ 60 — this threshold covers Gmail
and Outlook addresses, which always return 60 because those providers block
SMTP verification by design.
Pricing comparison
| Service | Price per email | Relative cost |
|---|---|---|
| ZeroBounce | $0.0080 | 10× more expensive |
| NeverBounce | $0.0070 | 8.7× more expensive |
| MillionVerifier | $0.0030 | 3.7× more expensive |
| emailval | $0.0008 | ✓ baseline |
No monthly fee. No minimum. Stripe charges your card at the end of each billing period for exactly what you used.
Usage
Filter valid emails from a list
from emailval import EmailValidator
ev = EmailValidator(api_key="your_api_key")
results = ev.validate(your_list)
valid_emails = [r.email for r in results if r.is_valid]
print(f"{len(valid_emails)} / {len(your_list)} are valid")
Get full metadata per email
for r in results:
print(r.email)
print(f" score: {r.score}")
print(f" valid_syntax: {r.valid_syntax}")
print(f" has_mx: {r.has_mx}")
print(f" is_disposable: {r.is_disposable}")
print(f" is_catch_all: {r.is_catch_all}")
print(f" smtp_result: {r.smtp_result}")
Large lists (auto-batched)
Lists of any size are split into batches of 500 automatically:
ev = EmailValidator(api_key="your_api_key")
results = ev.validate(your_million_email_list) # sends 2000 API calls
Batch response with metadata
resp = ev.validate_batch(emails[:500]) # max 500 per call
print(resp.processed) # 500
print(resp.valid_count) # e.g. 412
print(resp.invalid_count) # e.g. 88
print(resp.duration_ms) # e.g. 1243
# Filter helpers
valid = resp.valid() # list[EmailResult] with score >= 60
invalid = resp.invalid() # list[EmailResult] with score < 60
Skip SMTP for faster results (layers 1–3 only)
# Global — all calls skip SMTP
ev = EmailValidator(api_key="your_api_key", skip_smtp=True)
# Per-call override
results = ev.validate(emails, skip_smtp=True)
Useful when you need instant syntax + MX + disposable checks and don't need SMTP confirmation.
Async client (FastAPI, asyncio)
import asyncio
from emailval import AsyncEmailValidator
async def main():
async with AsyncEmailValidator(api_key="your_api_key") as ev:
results = await ev.validate(["user@example.com"])
for r in results:
print(r.email, r.score)
asyncio.run(main())
FastAPI middleware example
from fastapi import FastAPI
from emailval import AsyncEmailValidator
app = FastAPI()
ev = AsyncEmailValidator(api_key="your_api_key")
@app.post("/signup")
async def signup(email: str):
results = await ev.validate([email])
if not results[0].is_valid:
return {"error": "Invalid email address"}
# ... continue signup
Error handling
from emailval import EmailValidator
from emailval.exceptions import AuthenticationError, RateLimitError, APIError
ev = EmailValidator(api_key="your_api_key")
try:
results = ev.validate(["user@example.com"])
except AuthenticationError:
print("Invalid API key — check emailval.net for your key")
except RateLimitError:
print("Slow down — too many requests")
except APIError as e:
print(f"API error {e.status_code}: {e}")
Benchmark
Validating 10,000 emails with SMTP (layers 1–5):
Total: ~45 seconds (20 concurrent batches of 500)
Cost: $8.00
ZeroBounce: $80.00 for the same list
Validating 10,000 emails without SMTP (layers 1–3, skip_smtp=True):
Total: ~3 seconds
Cost: $8.00 (same price — charged per email regardless)
API reference
EmailValidator(api_key, base_url, timeout, skip_smtp)
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Your emailval API key |
base_url |
str |
https://api.emailval.net |
Override for testing |
timeout |
float |
60.0 |
Request timeout in seconds |
skip_smtp |
bool |
False |
Skip layers 4–5 globally |
ev.validate(emails, *, skip_smtp=None) → list[EmailResult]
Validate any number of emails. Large lists are split automatically.
ev.validate_batch(emails, *, skip_smtp=None) → ValidationResponse
Validate up to 500 emails. Returns metadata (duration, counts). Raises
BatchTooLargeError for lists > 500.
EmailResult fields
| Field | Type | Description |
|---|---|---|
email |
str |
The input email address |
score |
int |
Deliverability score 0–100 |
is_valid |
bool |
score >= 60 |
valid_syntax |
bool |
Passes RFC syntax check |
has_mx |
bool | None |
Domain has MX records |
is_disposable |
bool | None |
Known disposable provider |
is_catch_all |
bool | None |
Domain accepts all addresses |
smtp_result |
str | None |
accepted, rejected, inconclusive_*, skipped |
error |
str | None |
Processing error if any |
License
MIT. See LICENSE.
Links
- Website: emailval.net
- API docs: emailval.net/#api
- Issues: github.com/DmytroMosnenko/emailval-client/issues
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 emailval_client-1.0.0.tar.gz.
File metadata
- Download URL: emailval_client-1.0.0.tar.gz
- Upload date:
- Size: 9.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7af194395f0a5f16404d8f0e7031cbabb778c37cf38411e5b2e24d9a2244b038
|
|
| MD5 |
5770976e51dafc8489e63204ebc872eb
|
|
| BLAKE2b-256 |
6d0a52ed544ff472be672a5d002ba4794f4a0d2b8dafc97c6cad3a108267e123
|
Provenance
The following attestation bundles were made for emailval_client-1.0.0.tar.gz:
Publisher:
publish.yml on DmytroMosnenko/emailval-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
emailval_client-1.0.0.tar.gz -
Subject digest:
7af194395f0a5f16404d8f0e7031cbabb778c37cf38411e5b2e24d9a2244b038 - Sigstore transparency entry: 2245758096
- Sigstore integration time:
-
Permalink:
DmytroMosnenko/emailval-client@d45a5d4ae0dfcedf90e2cc14cf602d0efabf70dd -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/DmytroMosnenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d45a5d4ae0dfcedf90e2cc14cf602d0efabf70dd -
Trigger Event:
push
-
Statement type:
File details
Details for the file emailval_client-1.0.0-py3-none-any.whl.
File metadata
- Download URL: emailval_client-1.0.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3361ce460ad230b5969a88c5708a1f2f87fc0180294efc15e02aebba094695c2
|
|
| MD5 |
adf533f206e76acf101ae1617934bde0
|
|
| BLAKE2b-256 |
593f80a018878b606b832e3bf57f0de14d1b0ec96cc32dfb0f1b0b8482ed02d0
|
Provenance
The following attestation bundles were made for emailval_client-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on DmytroMosnenko/emailval-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
emailval_client-1.0.0-py3-none-any.whl -
Subject digest:
3361ce460ad230b5969a88c5708a1f2f87fc0180294efc15e02aebba094695c2 - Sigstore transparency entry: 2245758442
- Sigstore integration time:
-
Permalink:
DmytroMosnenko/emailval-client@d45a5d4ae0dfcedf90e2cc14cf602d0efabf70dd -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/DmytroMosnenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d45a5d4ae0dfcedf90e2cc14cf602d0efabf70dd -
Trigger Event:
push
-
Statement type: