The official Python client for the BounceZero email verification API. A single file, zero dependencies (standard library only), and a small, predictable surface designed for production workloads — from a signup form that validates one address in real time to a nightly job that cleans millions.
from bouncezero import BounceZero
client = BounceZero("bz_live_...")
result = client.verify("someone@example.com")
print(result["classification"], result["score"]) # → verified 98
Why BounceZero
Every address is scored across 40+ signals through a five-stage pipeline — syntax, DNS/MX, SMTP mailbox probing, disposable & role detection, and a Bayesian + ML confidence model — so you catch invalid, risky, and low-value addresses before they cost you deliverability.
- ⚡ Real-time single verification in ~500 ms–2 s, or async bulk for large lists
- 🎯 Six clear verdicts (
verified/invalid/catch_all/disposable/risky/unknown) plus threat flags - 🔁 Idempotent bulk submits — a network retry can never double-charge you
- 🧪 Sandbox keys to build & test without spending credits
- 🔒 Signed webhooks with a one-line verifier
- 📦 Zero dependencies, typed errors, automatic retries with backoff
Installation
pip install bouncezero
Requires Python 3.8+. No transitive dependencies.
Authentication
Grab your API key from the dashboard and pass it to the client. Keep live keys server-side — never ship them in client code.
from bouncezero import BounceZero
client = BounceZero("bz_live_...")
Quick start
# 1 — Verify a single address
result = client.verify("jane@example.com")
if result["classification"] == "verified":
add_to_mailing_list(result["email"])
# 2 — Batch up to 100 synchronously
report = client.verify_batch(["a@example.com", "b@example.com"])
print(report["summary"]) # {'verified': 1, 'invalid': 1, ...}
# 3 — Async bulk for large lists (idempotency-safe)
job = client.verify_bulk(emails, idempotency_key="import-2026-07-11")
final = client.wait_for_bulk(job["job_id"])
csv = client.bulk_download(job["job_id"])
Verification result
verify() returns a dict with the full signal breakdown. The fields you'll use most:
| Field | Type | Description |
|---|---|---|
email |
str |
The address that was checked |
classification |
str |
One of the six verdicts below |
score |
int |
Confidence 0–100 |
is_deliverable |
bool | None |
True/False, or None when inconclusive |
risk_level |
str |
low · medium · high |
recommendation |
str |
Human-readable guidance |
Classifications
| Verdict | Meaning | Safe to send? |
|---|---|---|
verified |
Mailbox exists and accepts mail | ✅ |
invalid |
Mailbox does not exist — will hard bounce | ❌ |
catch_all |
Domain accepts everything; mailbox unconfirmable | ⚠️ |
disposable |
Temporary/throwaway provider | ⚠️ |
risky |
Concrete negative signals (full mailbox, poor reputation…) | ⚠️ |
unknown |
Mail server blocked/greylisted the probe — retry later | ⏳ |
Two rare threat verdicts — complainer and spamtrap — may also appear; remove those addresses immediately (details in threat_type).
API reference
| Method | Description |
|---|---|
verify(email, depth="standard") |
Verify one address. depth: standard · deep · ultra |
verify_batch(emails) |
Verify up to 100 addresses synchronously |
verify_bulk(emails, idempotency_key=None) |
Submit an async bulk job |
bulk_status(job_id) |
Poll job progress |
bulk_results(job_id, limit=1000, offset=0) |
Paginated results |
bulk_download(job_id) |
Download completed results as CSV bytes |
wait_for_bulk(job_id, poll_interval=5.0, timeout=3600.0) |
Block until a job finishes |
domain(domain) |
Domain-level intelligence (MX, provider, catch-all…) |
analyze_list(emails) |
Free list-quality analysis — no verification, no credits |
BounceZero.verify_webhook_signature(body, header, secret) |
Validate a webhook signature |
Bulk verification
Bulk jobs run asynchronously. Submit, then either poll or wait:
job = client.verify_bulk(emails, idempotency_key="crm-sync-42")
# Option A — block until done
final = client.wait_for_bulk(job["job_id"], poll_interval=10)
# Option B — poll yourself
status = client.bulk_status(job["job_id"])
print(status["completed"], "/", status["total"])
# Fetch results (paginated) or download the full CSV
page = client.bulk_results(job["job_id"], limit=1000, offset=0)
csv = client.bulk_download(job["job_id"])
Idempotency — pass an idempotency_key (any unique string). Replaying the same key returns the original job instead of creating and charging for a duplicate, so retries after a timeout are always safe.
Error handling
Every failure raises a subclass of BounceZeroError, each carrying .status_code and .payload:
from bouncezero import (
BounceZero, AuthenticationError, InsufficientCreditsError,
RateLimitError, NotFoundError, BounceZeroError,
)
try:
result = client.verify("someone@example.com")
except InsufficientCreditsError:
top_up_credits()
except RateLimitError as e:
time.sleep(e.retry_after or 60)
except AuthenticationError:
alert("Check your API key")
except BounceZeroError as e:
log.error("BounceZero %s: %s", e.status_code, e)
| Exception | HTTP | When |
|---|---|---|
AuthenticationError |
401 / 403 | Missing, invalid, disabled, or IP-restricted key |
InsufficientCreditsError |
402 | Not enough credits |
NotFoundError |
404 | Unknown job or resource |
RateLimitError |
429 | Plan rate limit exceeded (see .retry_after) |
BounceZeroError |
other | Any other API/transport error |
Requests automatically retry on 429 and 5xx with exponential backoff (honoring Retry-After); tune with max_retries.
Rate limits
Limits are per API key, per minute, by plan. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
| Plan | Requests / min |
|---|---|
| Free | 30 |
| Starter · Growth | 100 |
| Professional · Business · Scale | 300 |
| Ultimate · Enterprise | 1000 |
Sandbox / testing
Create a sandbox key (bz_test_ prefix) in the dashboard. It returns deterministic mock results, never spends credits, and never performs a real probe — the local part of the address picks the verdict:
sandbox = BounceZero("bz_test_...")
sandbox.verify("verified@example.com")["classification"] # → "verified"
sandbox.verify("risky@example.com")["classification"] # → "risky"
Supported on verify(). See the sandbox docs.
Webhooks
BounceZero signs every webhook with X-BounceZero-Signature (HMAC-SHA256). Always verify against the raw request body before parsing:
from bouncezero import BounceZero
@app.post("/webhooks/bouncezero")
def handle(request):
if not BounceZero.verify_webhook_signature(
request.raw_body,
request.headers.get("X-BounceZero-Signature", ""),
signing_secret, # from Dashboard → Webhooks
):
return 400
event = request.json()
...
Configuration
client = BounceZero(
api_key="bz_live_...",
base_url="https://app.bouncezero.io", # override for a dedicated region
timeout=60, # seconds
max_retries=2, # retries on 429 / 5xx
)
Support
- 📖 Documentation — https://docs.bouncezero.io
- 📊 API status — https://status.bouncezero.io
- ✉️ support@bouncezero.io
License
MIT © BounceZero Ltd
Metadata
Release files for bouncezero 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bouncezero-0.1.0.tar.gz | 7.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bouncezero-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.2 kB
Release files / bouncezero-0.1.0.tar.gz
| Download URL | bouncezero-0.1.0.tar.gz |
|---|---|
| Size | 7.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07d376d590673f89473cc4ee2156ebbb45d4c02ccd0fbda3334d3bf578cb23e8
|
|
BLAKE2b-256 checksum How to use checksums |
c88e1e3832af8104f92bdc1ca8f39b9fcd222b6d9040825213da9646ad64d14c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 11, 2026.
Transparency logRelease files / bouncezero-0.1.0-py3-none-any.whl
| Download URL | bouncezero-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7997c3c1e21d5b524c035bc52fc42c3069f510323fcaeb76c638c92882ceb0ce
|
|
BLAKE2b-256 checksum How to use checksums |
d7c9d56e11a48dd5a350c21bdaf9e84c8cdf02f9c4b23beff410aa0871dbe458
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 11, 2026.
Transparency logSecurity for bouncezero 0.1.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for bouncezero-0.1.0.tar.gz
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 Jul 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.