NullMail for Python
The official typed Python client for the NullMail Pro API. Create temporary inboxes, list received messages, open full messages, and wait for verification codes or verification URLs from synchronous or asynchronous applications.
NullMail is receive-only and intended for legitimate privacy-conscious development and testing.
Requirements
- Python 3.9 or newer
- A NullMail Pro API key
Installation
pip install nullmail
The lightweight CLI is implemented with Python's standard library and can be installed using:
pip install "nullmail[cli]"
Authentication
Pass the API key directly:
from nullmail import NullMail
client = NullMail(api_key="YOUR_NULLMAIL_API_KEY")
For applications and the CLI, prefer the NULLMAIL_API_KEY environment variable.
Linux and macOS:
export NULLMAIL_API_KEY="your_api_key"
Windows PowerShell:
$env:NULLMAIL_API_KEY="your_api_key"
Then initialize without arguments:
client = NullMail()
The library never places the key in URLs and redacts it from exceptions. Do not commit API keys to source control.
API origin
Version 0.1.0 uses the production NullMail API at https://www.nullmail.xyz by default.
Self-hosted deployments can set another HTTPS origin explicitly:
client = NullMail(base_url="https://your-nullmail.example.com")
or:
export NULLMAIL_BASE_URL="https://your-nullmail.example.com"
HTTP is rejected for every host except localhost, 127.0.0.1, and ::1. TLS verification is
always enabled and redirects are not followed, preventing credentials from being forwarded to a
different host.
Create an inbox
from nullmail import NullMail
with NullMail() as client:
inbox = client.create_inbox("build-test")
print(inbox.email)
Allow the server to generate the username:
inbox = client.create_inbox()
Inbox creation is not automatically retried because the endpoint does not currently accept an idempotency key.
List messages
inbox = client.get_inbox("build-test@web-library.net")
for message in inbox.messages:
print(message.subject)
print(message.sender)
print(message.preview)
print(message.verification.code)
print(message.verification.url)
Verification values may be None when they are not present in the provider's message preview.
Read a full message
message = client.get_message(
message_id="MESSAGE_ID",
email="build-test@web-library.net",
)
print(message.subject)
print(message.text)
print(message.html)
print(message.verification.code)
print(message.verification.url)
The email address is an optional lookup hint. Full HTML email content is untrusted; do not render it without isolation and sanitization.
Wait for a verification email
verification = client.wait_for_verification(
email="build-test@web-library.net",
subject_contains="Verify",
sender_contains="example.com",
timeout=120,
interval=3,
)
print(verification.code)
print(verification.url)
The helper processes each message at most once, opens the full message when its summary lacks
verification details, and raises VerificationTimeoutError when the deadline is reached.
Async usage
import asyncio
from nullmail import AsyncNullMail
async def main() -> None:
async with AsyncNullMail() as client:
inbox = await client.create_inbox("build-test")
verification = await client.wait_for_verification(inbox.email, timeout=120)
print(verification.code)
asyncio.run(main())
Refresh an inbox
inbox = client.refresh_inbox("build-test@web-library.net")
Refresh requests are safely retryable but remain subject to the server's plan cooldown.
Error handling
from nullmail import AuthenticationError, RateLimitError
try:
inbox = client.create_inbox("build-test")
except AuthenticationError:
print("The API key is invalid or revoked.")
except RateLimitError as error:
print("Retry after:", error.retry_after)
The public hierarchy includes:
ConfigurationErrorAuthenticationErrorAuthorizationErrorValidationErrorNotFoundErrorRateLimitErrorAPIErrorConnectionErrorTimeoutErrorVerificationTimeoutError
API errors preserve safe status_code, request_id, error_code, and details attributes.
Configuration
client = NullMail(
api_key="...",
base_url="https://your-nullmail.example.com",
timeout=15,
max_retries=3,
)
Retries apply to temporary connection failures and HTTP 429, 500, 502, 503, and 504
responses for safe operations. The client uses exponential backoff and respects Retry-After.
CLI
The CLI reads the key only from NULLMAIL_API_KEY; it deliberately has no API-key argument.
nullmail create build-test
nullmail inbox build-test@web-library.net
nullmail message MESSAGE_ID --email build-test@web-library.net
nullmail wait build-test@web-library.net --subject Verify --timeout 120
Use --base-url or NULLMAIL_BASE_URL to target a self-hosted NullMail deployment.
Security recommendations
- Store API keys in a secret manager or environment variable.
- Never log client request headers or complete received email bodies.
- Treat sender names, subjects, message bodies, attachments, and verification URLs as untrusted.
- Validate a verification URL's destination before opening it.
- Revoke API keys that may have been exposed.
- Use HTTPS in every deployed environment.
Development
python -m venv .venv
.venv\Scripts\Activate.ps1 # Windows PowerShell
python -m pip install -e ".[dev]"
python -m pytest
python -m ruff check .
python -m ruff format --check .
python -m mypy nullmail
Tests use httpx.MockTransport and never call the live NullMail API.
Build and validate distributions:
python -m build
python -m twine check dist/*
License
MIT. See LICENSE.
Release files for nullmail 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 | |
|---|---|---|---|
| nullmail-0.1.0.tar.gz | 18.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nullmail-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.0 kB
Release files / nullmail-0.1.0.tar.gz
| Download URL | nullmail-0.1.0.tar.gz |
|---|---|
| Size | 18.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
69e11c90c16b3bef96d23a0d224f72ab5a6fc1534f6a416d9621cb004bd3f8b1
|
|
BLAKE2b-256 checksum How to use checksums |
611b6fcba51e7ec961dc31470a4c28796f569777fd57e27242fe9eaf4a7390c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|
Release files / nullmail-0.1.0-py3-none-any.whl
| Download URL | nullmail-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8a7830c607f7c6c8455793de1beae98c2ec61268539d1a368cc074c6d14a9cbd
|
|
BLAKE2b-256 checksum How to use checksums |
935b106a1a262dcdc7d5c30615f3d84f629e32808f473a5ce113c5ddf4df4505
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|