Skip to main content

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:

  • ConfigurationError
  • AuthenticationError
  • AuthorizationError
  • ValidationError
  • NotFoundError
  • RateLimitError
  • APIError
  • ConnectionError
  • TimeoutError
  • VerificationTimeoutError

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)

Source distribution for nullmail 0.1.0
File Size Uploaded
nullmail-0.1.0.tar.gz 18.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nullmail 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page