Metadata-Version: 2.4
Name: emailval-client
Version: 1.0.0
Summary: Python client for emailval.net — email validation API ($0.0008/email)
Author: Dmytro Mosnenko
License: MIT
Project-URL: Homepage, https://emailval.net
Project-URL: Documentation, https://emailval.net/#api
Project-URL: Repository, https://github.com/DmytroMosnenko/emailval-client
Project-URL: Bug Tracker, https://github.com/DmytroMosnenko/emailval-client/issues
Keywords: email,validation,verification,deliverability,email-validation,email-verification,smtp,mx,disposable-email,email-api
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: pytest<9,>=8.2; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: respx>=0.21; extra == "dev"
Dynamic: license-file

# emailval-client

Python client for [emailval.net](https://emailval.net) — email validation API.

**$0.0008 per email. 10× cheaper than ZeroBounce.**

[![PyPI version](https://img.shields.io/pypi/v/emailval-client)](https://pypi.org/project/emailval-client/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://pypi.org/project/emailval-client/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## Installation

```bash
pip install emailval-client
```

Requires Python 3.10+. The only dependency is `httpx`.

---

## Quickstart

```python
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](https://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

```python
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

```python
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:

```python
ev = EmailValidator(api_key="your_api_key")
results = ev.validate(your_million_email_list)  # sends 2000 API calls
```

### Batch response with metadata

```python
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)

```python
# 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)

```python
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

```python
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

```python
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](LICENSE).

## Links

- Website: [emailval.net](https://emailval.net)
- API docs: [emailval.net/#api](https://emailval.net/#api)
- Issues: [github.com/DmytroMosnenko/emailval-client/issues](https://github.com/DmytroMosnenko/emailval-client/issues)
