Disposable inboxes,over one API key.
The Nullz mail API is receive-only: create a mailbox, stream or poll it for messages, read or delete them. Copy-paste examples in cURL, JavaScript, Python and Go.
Quick start
Create a mailbox, then read it. Base URL: https://api.nullz.in
curl -X POST "https://api.nullz.in/api/v1/inbox" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"s3cret"}'
curl -X QUERY "https://api.nullz.in/api/v1/inbox" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"s3cret"}'
Authentication
Send your key on every request as X-API-Key. Create, view, rotate, or revoke it from your API keys page, where it stays masked until you ask to see it. Store it in an environment variable, never in client-side code.
X-API-Key: api_live_xxx
X-Mailbox-Password: s3cret # only for password-protected mailboxes
Content-Type: application/jsonA protected mailbox accepts the password either in the JSON body as password or in the X-Mailbox-Password header. Unprotected mailboxes can be read by anyone who knows the address — always set a password for anything real.
/api/v1/domainsList domains
Returns every domain you can create a mailbox on. Pick one before creating an inbox.
curl -X GET "https://api.nullz.in/api/v1/domains" \ -H "X-API-Key: $NULLZ_API_KEY"
{
"success": true,
"data": ["nullz.in", "mail.nullz.in"]
}/api/v1/inboxCreate a mailbox
Registers a receive-only mailbox. Add a password to lock the mailbox so only requests carrying that password can read it.
curl -X POST "https://api.nullz.in/api/v1/inbox" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"optional-mailbox-password","label":"optional label"}'
{
"email": "demo@nullz.in",
"password": "optional-mailbox-password",
"label": "optional label"
}{
"success": true,
"data": { "email": "demo@nullz.in", "protected": true }
}- —409 means the address already exists — reuse it with the same password.
- —Mailboxes are receive-only. There is no send endpoint.
/api/v1/inboxList messages
Reads are done with the QUERY method and a JSON body, so mailbox credentials never end up in a URL or an access log.
curl -X QUERY "https://api.nullz.in/api/v1/inbox" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"mailbox-password-if-protected"}'
{
"email": "demo@nullz.in",
"password": "mailbox-password-if-protected"
}{
"success": true,
"data": [
{
"id": "msg_01H...",
"from": "no-reply@example.com",
"subject": "Your code is 481920",
"receivedAt": "2026-07-31T16:40:11.000Z",
"hasAttachments": false
}
]
}- —404 means the mailbox is not registered — create it first.
/api/v1/inbox/messageRead one message
Fetches the full message, including plain-text and HTML bodies. Empty bodies come back as null, never as a fake empty string.
curl -X QUERY "https://api.nullz.in/api/v1/inbox/message" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id":"msg_01H...","email":"demo@nullz.in","password":"mailbox-password-if-protected"}'
{
"id": "msg_01H...",
"email": "demo@nullz.in",
"password": "mailbox-password-if-protected"
}{
"success": true,
"data": {
"id": "msg_01H...",
"from": "no-reply@example.com",
"subject": "Your code is 481920",
"textContent": "Your code is 481920",
"htmlContent": "<p>Your code is <b>481920</b></p>"
}
}/api/v1/inbox/streamMint a live-stream token
Live updates beat polling: mint a short-lived token (~90s) that authorizes opening one Server-Sent Events stream for the mailbox. Same access rules as the other mail routes.
curl -X POST "https://api.nullz.in/api/v1/inbox/stream" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"mailbox-password-if-protected"}'
{
"email": "demo@nullz.in",
"password": "mailbox-password-if-protected"
}{
"success": true,
"data": {
"token": "eyJhbGciOi...",
"expiresIn": 90,
"email": "demo@nullz.in",
"streamPath": "/api/v1/inbox/stream",
"maxStreamSeconds": 900,
"heartbeatSeconds": 20
}
}- —Treat the token as a one-shot credential — it can appear in access logs.
- —Limits: 3 concurrent streams per IP, 2 per mailbox.
/api/v1/inbox/stream?token=…Live inbox stream (SSE)
Opens a text/event-stream connection. No API key or custom headers needed, so browser EventSource works directly. The stream ends after ~15 minutes — re-mint a token and reconnect.
event: ready
data: { "email": "demo@nullz.in" }
event: snapshot
data: [ { "id": "msg_01H...", "subject": "Your code is 481920" } ]
event: message
data: { "id": "msg_02J...", "subject": "Verify your login",
"fromAddress": "no-reply@example.com",
"toAddress": "demo@nullz.in",
"createdAt": "2026-07-31T17:02:44.000Z" }
event: heartbeat
data: { "ts": 1785518564 }
event: end
data: { "reason": "max_duration" }- —Events: ready, snapshot, message, heartbeat (~20s), end.
- —The token may also be sent as Authorization: Bearer <token>.
- —If you must poll instead, use 5–10s+ intervals and honour retryAfter.
/api/v1/inbox/messageDelete a message
Permanently removes a single message from the mailbox.
curl -X DELETE "https://api.nullz.in/api/v1/inbox/message" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id":"msg_01H...","email":"demo@nullz.in","password":"mailbox-password-if-protected"}'
{
"id": "msg_01H...",
"email": "demo@nullz.in",
"password": "mailbox-password-if-protected"
}{ "success": true }/api/v1/inboxDelete a mailbox
Destroys the mailbox and every message it holds.
curl -X DELETE "https://api.nullz.in/api/v1/inbox" \ -H "X-API-Key: $NULLZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"demo@nullz.in","password":"mailbox-password-if-protected"}'
{
"email": "demo@nullz.in",
"password": "mailbox-password-if-protected"
}{ "success": true }Errors
Every failure — including unhandled 404s — returns { "success": false, "error": "message" }.
400Malformed body — a required field is missing or not a string.401Missing, revoked, or invalid X-API-Key.403Wrong mailbox password, or the caller has no key, JWT or allowed origin.404Mailbox or message does not exist.409Mailbox already exists (on create).429Rate limit exceeded — read retryAfter / Retry-After and back off.Rate limits
Current defaults: unauthenticated website traffic shares a 2s minimum interval across v1 calls, mailbox creation is limited to one every 4s per IP (create is not stacked on the list interval), API keys get 60 requests/minute, and each IP has a 60/minute baseline. Auth allows 5 registrations/hour and 10 logins/minute. Every 429 returns { "success": false, "error": "rate limit exceeded", "retryAfter": N } plus a Retry-After header — back off exponentially, prefer SSE, and if you must poll, use 5–10s or slower.
let delay = 10_000; for (;;) { const res = await poll(); if (res.status === 429) { const { retryAfter } = await res.json(); delay = Math.min(Math.max((retryAfter ?? 5) * 1000, delay * 2), 120_000); } else { delay = 10_000; } await new Promise((r) => setTimeout(r, delay)); }
Mailboxes are receive-only and messages are pruned automatically. Abuse reports go to abuse@nullz.in.
