Temp Mail API
An HTTP API for disposable inboxes. It exists for one job: your code needs an email address, waits for a message, pulls a confirmation code out of it and moves on. Signup tests, agent workflows, throwaway registrations in a sandbox.
Two things here are unusual for this corner of the market. Messages are pushed over server-sent events, so nothing polls in a loop. And there is an endpoint that returns the confirmation code itself, so you do not write another regular expression for another vendor's email template.
Getting a key
Create an account, open your account and press Create key. The value is shown once. It is stored as a hash, so nobody can recover it later, not even us: lose it and you issue a new one.
Send it as a header. Keys are not accepted in the query string, because query strings end up in proxy logs and browser history, and the owner is the last to find out.
Authorization: Bearer tm_your_key_here
Quick start
# 1. take an address
curl -s -X POST https://tempmailgenerator.net/api/v1/mailboxes \
-H "Authorization: Bearer $TEMPMAIL_KEY"
# => {"mailbox":{"id":"clx...","address":"laura.ellis5427@...","expiresAt":"..."}}
# 2. trigger the signup in your app, then wait for the code
curl -s "https://tempmailgenerator.net/api/v1/mailboxes/clx.../code?wait=60" \
-H "Authorization: Bearer $TEMPMAIL_KEY"
# => {"code":"481902","link":"https://shop.example/confirm?t=abc","message":{...}}
That second call blocks until the message lands, up to the timeout you ask for. It is not polling underneath: the connection sits idle and costs nothing until an email actually arrives.
Endpoints
| Method and path | What it does |
|---|---|
POST /api/v1/mailboxes | Create an inbox. Optional body: localPart, domainId. |
GET /api/v1/mailboxes/{id} | Address and expiry. |
DELETE /api/v1/mailboxes/{id} | Delete the inbox and its messages. |
GET /api/v1/mailboxes/{id}/messages | Message list, newest first, without bodies. |
GET /api/v1/mailboxes/{id}/code | Wait for a message and return the code and link. |
GET /api/v1/mailboxes/{id}/stream | Server-sent events, one per new message. |
GET /api/v1/messages/{id} | Full message: text, HTML, attachments. |
GET /api/v1/domains | Domains this API hands out addresses on. |
Waiting for a code
Parameters on /code:
wait- seconds to hold the connection, default 30, maximum 120. Passwait=0to check what is already there and return immediately.from- only consider messages whose sender contains this string. Useful when a test triggers two emails at once.since- ISO timestamp; ignore anything older. Take it before you trigger the signup and a leftover message from a previous run cannot fool the test.
The code is looked for in the subject first and then in the body near words
like code, pin or otp. Four to eight digits, with plain years discarded. If
nothing matches, code comes back null while
link and the message itself are still there.
Streaming
const events = new EventSource(url, { headers });
events.addEventListener("message", (event) => {
const { messageId } = JSON.parse(event.data);
// fetch /api/v1/messages/{messageId} for the body
});
A comment line arrives every 25 seconds to keep proxies from closing an idle connection. Ignore it.
Limits
- 300 inbox creations per hour and 600 reads per minute, counted per key.
- 100 live inboxes per key. Delete what you are done with, or let them expire.
- Inboxes live 24 hours from creation. Reading does not extend that: a test that polls for an hour should not keep its inbox alive for a week.
- Messages are deleted with their inbox, and never live longer than 30 days.
Rate limited requests answer 429 with a retry-after
header. Every error looks the same:
{"error":{"code":"rate-limited","message":"Too many requests"}}
Codes you can branch on: unauthorized, rate-limited,
not-found, invalid-request, limit-reached,
domain-not-available, name-taken,
name-invalid, name-reserved.
Domains
The API hands out addresses on its own pool, separate from the domains the website offers. Integrators burn domains far faster than people do: a few thousand signups and the domain lands on every disposable blocklist. Keeping the pools apart means that burn does not reach the visitors on the site, and your tests do not inherit a domain that someone else already exhausted.
A consequence worth knowing: addresses from this API are recognisable as disposable. It is a tool for testing your own systems, not a way around someone else's rules.
What we ask
Use it for your own products: test suites, staging environments, agents you control. Do not point it at other people's services to farm accounts or defeat their limits. That is the line where this stops being free and starts being somebody's incident.