βββββββ βββ βββββββββββββββββββββββ
ββββββββββββ ββββββββββββββββββββββββ
ββββββββ βββββββ βββββ βββ βββ
βββββββ βββββ βββββ βββ βββ
βββ βββ βββββββββββ βββ
βββ βββ βββββββββββ βββ
βββββββββββββββββββββ βββββββββββ ββββ ββββ ββββββ ββββββ
ββββββββββββββββββββββ ββββββββββββββββββ βββββββββββββββββββ
βββ ββββββ ββββββββββββββββββββββββββββββββββββββββββββ
βββ ββββββ ββββββββββββββββββ βββββββββββββββββββββββββ
βββ βββββββββββ βββ ββββββ βββ βββ ββββββ ββββββββββββββ
βββ βββββββββββ ββββββ βββ ββββββ ββββββββββββββ
The official Python SDK for the Pyzit Disposable Email Detector API
Stop fake signups. Block throwaway addresses. Protect your platform.
tempmail-pyzit is the official Python client for the Pyzit Disposable Email API β a service that detects throwaway, temporary, and fake email addresses in real time.
User submits email SDK validates it Your app decides
βββββββββββββββββ βββββββββββββββββ βββββββββββββββ
user@mailnator.com βββββββΊ is_disposable: True βββββββΊ β Reject signup
hi@yourcompany.com βββββββΊ is_disposable: False βββββββΊ β Allow signup
Works with Django, Flask, FastAPI, plain scripts β sync and async both supported out of the box.
- Installation
- Quick start
- API tiers
- All methods
- Async usage
- Response models
- Error handling
- Framework integrations
- Configuration
- Development
pip install tempmail-pyzitOr with uv (recommended):
uv add tempmail-pyzitRequirements: Python 3.9 or higher.
from tempmail_pyzit import TempMailClient
client = TempMailClient("YOUR_API_TOKEN")
result = client.check("user@example.com")
if result.is_disposable:
print("β Disposable email β rejected")
else:
print("β
Looks clean β allowed")Get your API token at temp-mail-detector.pyzit.com.
Tip: Store your token in an environment variable, never hard-code it.
import os client = TempMailClient(os.environ["PYZIT_TOKEN"])
Three endpoints, three plan levels. Use only what you need.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ENDPOINT OVERVIEW β
ββββββββββββββββββββ¬ββββββββββββββ¬βββββββββββββββββββββββββββββββββββ€
β Method β Plan β What you get β
ββββββββββββββββββββΌββββββββββββββΌβββββββββββββββββββββββββββββββββββ€
β client.check() β Free β is_disposable + status β
β client.detailed()β Pro β DNS, signals, risk score, reco β
β client.bulk() β Business β Up to 100 emails, one request β
ββββββββββββββββββββ΄ββββββββββββββ΄βββββββββββββββββββββββββββββββββββ
The fastest check. One email in, one decision out. Free tier.
result = client.check("user@mailnator.com")
result.email # "user@mailnator.com"
result.is_disposable # True
result.status # "disposable"
result.is_clean # False (convenience property, opposite of is_disposable)Response shape:
CheckResult
βββ email str β the email you submitted
βββ is_disposable bool β True = block it
βββ status str β "clean" | "disposable"
βββ is_clean bool β shorthand for not is_disposable
Full forensic analysis. DNS records, SMTP probing, reputation scoring, domain age, signal breakdown. Pro tier.
result = client.detailed("suspicious@new-domain.io")
result.risk_level # "high"
result.recommendation # "reject"
result.reputation_score # 0.0
result.should_reject # True (convenience property)
# DNS intelligence
result.details.dns_intelligence.has_mx # False
result.details.dns_intelligence.has_spf # False
# Signal breakdown
result.details.signals.negative # ["no_mx_records", "new_domain", ...]
result.details.signals.positive # []
# Domain stability
result.details.stability.domain_age_days # 0
result.details.stability.is_new_domain # TrueResponse shape:
DetailedResult
βββ email str
βββ domain str
βββ is_disposable bool
βββ status str
βββ reputation_score float β 0.0 (worst) to 1.0 (best)
βββ risk_level str β "low" | "medium" | "high"
βββ recommendation str β "accept" | "review" | "reject"
βββ should_reject bool β shorthand: recommendation == "reject"
βββ details
βββ reputation
β βββ reputation_score float
β βββ is_disposable bool
β βββ disposable_confidence float
β βββ risk_level str
β βββ recommendation str
βββ signals
β βββ positive list[str] β trust signals
β βββ negative list[str] β risk signals
β βββ neutral list[str] β ambiguous signals
βββ dns_intelligence
β βββ has_mx bool
β βββ mx_records list[str]
β βββ has_a_record bool
β βββ has_spf bool
β βββ has_dmarc bool
β βββ error str | None
βββ stability
βββ stability_score float
βββ domain_age_days int
βββ is_new_domain bool
βββ risk_factors list[str]
Validate up to 100 emails in a single API call. Business tier.
result = client.bulk([
"hi@pyzit.com",
"cyz@temp-mail.org",
"user@mailnator.com",
])
result.processed # 3
result.results # {"hi@pyzit.com": False, "cyz@temp-mail.org": True, ...}
result.disposable_emails() # ["cyz@temp-mail.org", "user@mailnator.com"]
result.clean_emails() # ["hi@pyzit.com"]Response shape:
BulkResult
βββ results dict[str, bool] β {email: is_disposable}
βββ processed int β number of emails processed
βββ disposable_emails() list[str] β emails where is_disposable=True
βββ clean_emails() list[str] β emails where is_disposable=False
AsyncTempMailClient is a drop-in replacement for async codebases.
The method signatures are identical β just add await.
from tempmail_pyzit import AsyncTempMailClient
client = AsyncTempMailClient("YOUR_API_TOKEN")
# anywhere inside an async function:
result = await client.check("user@example.com")
result = await client.detailed("user@example.com")
result = await client.bulk(["a@x.com", "b@y.com"])Both clients share the same response models, exceptions, and behaviour.
There is no functional difference other than async/await.
All responses are fully typed Pydantic v2 models. You get autocomplete, runtime validation, and type safety for free.
tempmail_pyzit.models
β
βββ CheckResult β client.check()
βββ DetailedResult β client.detailed()
β βββ DetailedDetails
β βββ ReputationDetail
β βββ Signals
β βββ DnsIntelligence
β βββ StabilityInfo
βββ BulkResult β client.bulk()
All SDK errors inherit from PyzitError, so you can catch everything
at one level or be specific.
PyzitError β catch-all for any SDK error
βββ AuthenticationError β HTTP 401 β bad or missing token
βββ PlanRequiredError β HTTP 402/403 β need a higher plan
β βββ .required_plan β str, e.g. "pro" or "business"
βββ RateLimitError β HTTP 429 β slow down
β βββ .retry_after β int, seconds to wait
βββ APIError β any other non-2xx response
β βββ .status_code β int
β βββ .response_body β str, raw body for debugging
βββ TimeoutError β request took too long
Recommended pattern β catch specific errors:
from tempmail_pyzit import (
TempMailClient,
AuthenticationError,
PlanRequiredError,
RateLimitError,
APIError,
TimeoutError,
PyzitError,
)
import time
client = TempMailClient("YOUR_TOKEN")
try:
result = client.check("user@example.com")
except AuthenticationError:
# token is wrong β fail fast, don't retry
raise SystemExit("Check your PYZIT_TOKEN environment variable.")
except PlanRequiredError as e:
# you called an endpoint above your plan
print(f"Upgrade to {e.required_plan} to use this feature.")
except RateLimitError as e:
# back off and retry
time.sleep(e.retry_after)
result = client.check("user@example.com")
except TimeoutError:
# API too slow β decide how to handle in your app
print("Pyzit API timed out β allowing email through.")
except APIError as e:
# unexpected server error
print(f"Unexpected error {e.status_code}: {e.response_body}")
except PyzitError:
# fallback for any other SDK error
print("Something went wrong with the Pyzit SDK.")Minimal pattern β just block disposables, ignore errors gracefully:
def is_allowed(email: str) -> bool:
try:
return not client.check(email).is_disposable
except PyzitError:
return True # fail open β let the email through if API is down# validators.py
from django.core.exceptions import ValidationError
from tempmail_pyzit import TempMailClient, PyzitError
import os
_client = TempMailClient(os.environ["PYZIT_TOKEN"])
def validate_no_disposable_email(value: str) -> None:
try:
result = _client.check(value)
if result.is_disposable:
raise ValidationError(
"Disposable email addresses are not allowed. "
"Please use your real email."
)
except PyzitError:
pass # fail open if API is unreachable
# models.py
from django.db import models
from .validators import validate_no_disposable_email
class UserProfile(models.Model):
email = models.EmailField(validators=[validate_no_disposable_email])from fastapi import FastAPI, HTTPException
from tempmail_pyzit import AsyncTempMailClient, PyzitError
import os
app = FastAPI()
client = AsyncTempMailClient(os.environ["PYZIT_TOKEN"])
@app.post("/register")
async def register(email: str):
try:
result = await client.check(email)
except PyzitError:
pass # fail open
else:
if result.is_disposable:
raise HTTPException(
status_code=422,
detail="Disposable email addresses are not permitted."
)
return {"status": "ok", "email": email}from flask import Flask, request, jsonify
from tempmail_pyzit import TempMailClient, PyzitError
import os
app = Flask(__name__)
client = TempMailClient(os.environ["PYZIT_TOKEN"])
@app.route("/register", methods=["POST"])
def register():
email = request.json.get("email", "")
try:
result = client.check(email)
if result.is_disposable:
return jsonify({"error": "Disposable emails not allowed."}), 422
except PyzitError:
pass # fail open
return jsonify({"status": "ok"})Both TempMailClient and AsyncTempMailClient accept the same options:
client = TempMailClient(
api_token = "YOUR_TOKEN", # required
timeout = 10.0, # seconds, default 10.0
base_url = "https://api-tempmail.pyzit.com/v1", # override for testing
)| Parameter | Type | Default | Description |
|---|---|---|---|
api_token |
str |
β | Your Pyzit API token (required) |
timeout |
float |
10.0 |
Request timeout in seconds |
base_url |
str |
https://api-tempmail.pyzit.com/v1 |
Override for local testing / mocks |
This project is built with uv.
# clone
git clone https://github.com/pyzit-org/temp-mail-detector-sdk-python.git
cd temp-mail-detector-sdk-python
# install all dev dependencies
uv sync --extra dev
# run tests (no real API calls β all HTTP is mocked)
uv run pytest -v
# lint
uv run ruff check . --fix
uv run ruff format .
# type check (strict mypy)
uv run mypy src/
# build distribution
uv buildProject layout:
src/tempmail_pyzit/
βββ __init__.py β public API surface
βββ exceptions.py β all custom error classes
βββ models.py β pydantic response types
βββ _http.py β shared transport (headers, error handling)
βββ client.py β TempMailClient (sync)
βββ async_client.py β AsyncTempMailClient (async)
tests/
βββ conftest.py β shared fixtures
βββ test_client.py β sync client tests
βββ test_async_client.py
βββ test_models.py
See CHANGELOG.md.
Built with care by the Pyzit team Β· https://pyzit.com