zerodrop
Email verification infrastructure for CI pipelines and AI agents.
Send a verification email. Catch it at the edge. Get email.otp and email.magic_link back — auto-extracted, no regex, no Docker, no signup.
email = mail.wait_for_latest(inbox)
email.otp # "123456" — auto-extracted
email.magic_link # "https://..." — no regex needed
Documentation · GitHub · Status
Install
pip install zerodrop
No dependencies. Python 3.8+.
Zero-Auth Mode (Local Development)
from zerodrop import ZeroDrop
mail = ZeroDrop()
inbox = mail.generate_inbox()
# → "swift-x7k29@zerodrop-sandbox.online"
email = mail.wait_for_latest(inbox, timeout=10000)
print(email.subject) # "Reset your password"
print(email.otp) # "123456" — auto-extracted, no regex needed
print(email.magic_link) # "https://..." — auto-extracted verification link
CI Pipeline Mode (pytest)
from zerodrop import ZeroDrop
mail = ZeroDrop(api_key=os.environ.get("ZERODROP_API_KEY"))
def test_password_reset(page):
inbox = mail.generate_inbox()
page.goto("/forgot-password")
page.fill('[name="email"]', inbox)
page.click('[type="submit"]')
email = mail.wait_for_latest(inbox, timeout=15000)
assert "Reset your password" in email.subject
# No regex — magic_link is auto-extracted at the edge
page.goto(email.magic_link)
assert page.url == "/dashboard"
Email Filtering
Filter emails by sender, subject, body, or extracted fields:
from zerodrop import ZeroDrop, ZeroDropFilter
mail = ZeroDrop()
# Only match emails from a specific sender
email = mail.wait_for_latest(inbox, filter_=ZeroDropFilter(
from_="noreply@yourapp.com"
))
# Only match emails with an OTP
email = mail.wait_for_latest(inbox, filter_=ZeroDropFilter(
has_otp=True
))
# Combine filters
email = mail.wait_for_latest(inbox, timeout=15000, filter_=ZeroDropFilter(
from_="noreply@yourapp.com",
subject="Verify",
has_magic_link=True
))
All string filters are case-insensitive partial matches.
OTP Auto-Extraction
ZeroDrop extracts OTP codes and magic links at the edge before emails reach your test suite. No regex required.
email = mail.wait_for_latest(inbox)
email.otp # "123456" — 4-8 digit verification code
email.magic_link # "https://app.com/verify?token=abc" — verification/reset link
email.body # Full plain-text body (if you need it)
Both fields are None if not detected in the email.
Parallel CI Runs
generate_inbox() runs locally — no network request, no throttling:
# Safe to run in parallel — generate_inbox() is local
inboxes = [mail.generate_inbox() for _ in range(50)]
API
ZeroDrop(api_key=None, base_url=BASE_URL)
api_key— optional. Omit for free sandbox mode.base_url— override the API base URL.
mail.generate_inbox() -> str
Returns a ready-to-use email address instantly. No network request.
mail.fetch_latest(inbox, filter_=None) -> Optional[ZeroDropEmail]
Returns the latest email or None if inbox is empty.
mail.wait_for_latest(inbox, timeout=10000, poll_interval=2.0, filter_=None) -> ZeroDropEmail
Polls until an email arrives. Raises ZeroDropTimeoutError on timeout.
timeout— milliseconds to wait (default: 10000)poll_interval— seconds between polls (default: 2.0)filter_— optionalZeroDropFilterinstance
mail.on_received(inbox, webhook_url) -> dict
Registers a webhook. Requires API key (Workspace tier).
Types
@dataclass
class ZeroDropEmail:
id: str
from_: str
to: str
subject: str
body: str
raw_body: str
received_at: datetime
otp: Optional[str] # Auto-extracted OTP code (4-8 digits)
magic_link: Optional[str] # Auto-extracted verification/reset link
@dataclass
class ZeroDropFilter:
from_: Optional[str] = None # Partial match on sender address
subject: Optional[str] = None # Partial match on subject line
body: Optional[str] = None # Partial match on email body
has_otp: Optional[bool] = None # Only match emails with an OTP
has_magic_link: Optional[bool] = None # Only match emails with a magic link
Error Handling
from zerodrop import ZeroDrop, ZeroDropTimeoutError
try:
email = mail.wait_for_latest(inbox, timeout=10000)
except ZeroDropTimeoutError:
print("No email received — check your app is sending correctly")
Free vs Workspace
| Free | Workspace | |
|---|---|---|
| Inbox generation | ✓ | ✓ |
| OTP auto-extraction | ✓ | ✓ |
| Magic link extraction | ✓ | ✓ |
| Email filtering | ✓ | ✓ |
| Email retention | 30 min | Extended |
| Custom domains | ✗ | ✓ |
| API key | ✗ | ✓ |
| Webhooks | ✗ | ✓ |
| AI spam filter | On | Off |
Get a Workspace at zerodrop.dev
License
MIT
Release files for zerodrop 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 | |
|---|---|---|---|
| zerodrop-0.1.0.tar.gz | 6.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zerodrop-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.3 kB
Release files / zerodrop-0.1.0.tar.gz
| Download URL | zerodrop-0.1.0.tar.gz |
|---|---|
| Size | 6.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b1afa0b2726ebdde5a67040fe994fbb8a4d403896cb68d11e3b80af585cb05c9
|
|
BLAKE2b-256 checksum How to use checksums |
8d805b40ed6f6b0eb6abc600e92db6c60555ead75c76509cb1b2ff57a8907300
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|
Release files / zerodrop-0.1.0-py3-none-any.whl
| Download URL | zerodrop-0.1.0-py3-none-any.whl |
|---|---|
| Size | 7.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
114c24e087f54bf790cbbef5588d14c0dc6be8881abf4d8c5c5e1f3c905669cf
|
|
BLAKE2b-256 checksum How to use checksums |
f2f883955e0a0d17efdc51b92be2419bc8ce28f4a14128ed1c86d2d1f87fce4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|