The Python client is a thin wrapper over the REST API: one method per endpoint, the API's data and meta blocks returned exactly as documented, and a typed exception for every error code. It has no dependencies beyond the standard library, ships type hints, and works on Python 3.9 and newer.
The package is built and will be published on PyPI as spaw-sdk; the distribution name differs from the import name because spaw was already taken. Until the listing is live, the plain REST calls in the API reference do the same job with a few more lines.
Install and verify one address
- Create a secret key on the API keys page.
- Install the package once it is published:
pip install spaw-sdk
- Verify an address. The import name is
spaw:
from spaw import Client
client = Client("sk_live_…")
result = client.verify_email("[email protected]")
print(result.data["deliverable"]) # "deliverable", "risky" or "undeliverable"
print(result.data["reason"]) # e.g. "catch_all", or None when deliverable
print(result.data["risk_score"]) # 0–100, a sum of published weights
print(result.meta["credits_used"]) # 1 for a fresh answered lookup, 0 otherwise
Every reason and smtp_reason value is explained on the verdict reasons pages, and result.data carries all 27 documented fields, including did_you_mean, mx_provider and smtp_checked_at.
Everything the client does
| Method | Endpoint |
|---|---|
verify_email(email, callback_url=None, callback_secret=None) |
POST /email |
verify_emails(emails) |
POST /email/batch, up to 50 |
domain(domain) |
GET /email/domain/{domain} |
create_bulk_job(emails, webhook_url=None), get_bulk_job(id), cancel_bulk_job(id), download_bulk_results(id, variant=None) |
the bulk endpoints, up to 100,000 addresses |
list_suppressions(page=1), add_suppressions(emails), remove_suppression(id) |
the suppression list |
report_feedback(items), feedback_summary() |
delivery outcomes and measured accuracy |
account() |
the live credit balance |
validate_phone(phone, country=None), lookup_ip(ip) |
the phone and IP endpoints |
A batch keeps the API's shape: data["results"] holds one {data, meta} per address in input order, and meta["stopped_reason"] is set instead of an exception when the balance runs out part-way, so paid results are never lost.
Handle errors
Every failure raises a subclass of SpawError carrying code, message, http_status and request_id; no credits are spent on a failed request.
from spaw import InsufficientCreditsError, RateLimitedError, SpawError
try:
result = client.verify_email("[email protected]")
except InsufficientCreditsError:
... # nothing ran and nothing was billed
except RateLimitedError as error:
... # error.retry_after seconds, from the Retry-After header
except SpawError as error:
print(error.code, error.http_status, error.request_id)
AuthenticationError maps UNAUTHENTICATED, ValidationError maps VALIDATION_FAILED with errors keyed by field, NotFoundError covers a bulk job or suppression entry that is not on the account, and SpawConnectionError means the API could not be reached. Pass max_retries=2 to the client to retry 429 and 5xx answers automatically, honouring Retry-After.
What to do with the verdict
Branch on deliverable. Keep deliverable addresses, drop undeliverable ones, and route risky ones by reason: disposable and likely_typo are usually refused, role depends on your use case, and catch_all is often a fine address whose mailbox simply cannot be confirmed, in which case mailbox_confidence gives you a number to threshold. Offer did_you_mean back to the user when it is set.
Cost
One credit per fresh deliverable or risky verdict. Undeliverable verdicts, invalid input, repeats within seven days and the spaw.test test addresses cost nothing, and every account gets 10 free lookups a month. Use [email protected] and its siblings in your test suite: they answer fixed results, cost nothing and never appear in your history.