v1 · stable
fmail API
A tiny REST API for reading disposable inboxes. Free, public, no key. Use it for testing flows, harvesting OTP codes, or grabbing one-shot verifications from a script.
Base URL
https://fmail.men/v1
Auth
none
CORS
open · *
Rate limit
fair use
Quick start
Generate a random address, send mail to it, then read the inbox. Three calls.
# 1. get a random address
curl https://fmail.men/v1/random
# 2. send mail to it (from your normal email client)
# 3. read the inbox a few seconds later
curl https://fmail.men/v1/inbox/your-name?domain=fmail.men
const base = "https://fmail.men/v1";
const { address, username, domain } = await fetch(`${base}/random`).then(r => r.json());
console.log("Send mail to:", address);
// ... wait or poll ...
const inbox = await fetch(`${base}/inbox/${username}?domain=${domain}`).then(r => r.json());
console.log(inbox.emails);
import requests
base = "https://fmail.men/v1"
addr = requests.get(f"{base}/random").json()
print("Send mail to:", addr["address"])
# ... wait or poll ...
inbox = requests.get(f"{base}/inbox/{addr['username']}", params={"domain": addr["domain"]}).json()
print(inbox["emails"])
Authentication & limits
There is no authentication. Every endpoint is publicly readable. Anyone who knows a username+domain can read its inbox — that's the whole point of a disposable inbox service.
- Up to 50 emails per inbox. Older ones drop off.
- Mail is deleted after 7 days.
- Username must match
[a-z0-9._-]{1,64}. - Be reasonable with polling — every 5 seconds is plenty.
Errors
Errors come back as JSON with an error object and a non-2xx status:
HTTP/1.1 400 Bad Request
content-type: application/json
{
"error": {
"code": "invalid_username",
"message": "Username must match [a-z0-9._-]{1,64}"
}
}
| Code | Status | Meaning |
|---|---|---|
invalid_username | 400 | Bad username format |
not_found | 404 | Email id doesn't exist (or endpoint doesn't) |
method_not_allowed | 405 | Used wrong HTTP method |
Reference
Six endpoints. All return JSON. All support CORS.
/v1/health
Returns { "ok": true } if the API is up. Use it for a heartbeat.
curl https://fmail.men/v1/health
await fetch("https://fmail.men/v1/health").then(r => r.json());
// => { ok: true }
requests.get("https://fmail.men/v1/health").json()
# => {'ok': True}
/v1/domains
List every domain you can receive on. The default is what the web app picks if none is specified.
Response
{
"domains": ["fmail.men", "exolinker.com", "uncmail.org", "sevril.win", "www-guns.lol", "deislerlive.com", "guns.lat", "kuruptd.ink", "ougoods.com"],
"default": "fmail.men"
}
/v1/random
Generate a random username on a chosen domain. Saves you from having to think one up.
| Query | Type | Default | Notes |
|---|---|---|---|
domain | string | fmail.men | Must be one returned by /v1/domains |
Response
{
"username": "drift4471",
"domain": "fmail.men",
"address": "drift4471@fmail.men"
}
/v1/inbox/:username
List emails in an inbox, newest first. Email bodies aren't included — use /v1/email/:token to fetch the full content.
| Param | Type | Default | Notes |
|---|---|---|---|
:username | path | — | Local part of the address |
domain | query | fmail.men | Pick the domain |
since | query · ms | — | Only emails received after this timestamp |
limit | query · 1–50 | 50 | Cap the result |
curl "https://fmail.men/v1/inbox/drift4471?domain=fmail.men&limit=10"
const u = "drift4471";
const r = await fetch(`https://fmail.men/v1/inbox/${u}?domain=fmail.men&limit=10`);
const data = await r.json();
for (const e of data.emails) console.log(e.token, e.sender, e.subject);
r = requests.get(
"https://fmail.men/v1/inbox/drift4471",
params={"domain": "fmail.men", "limit": 10},
)
for e in r.json()["emails"]:
print(e["token"], e["sender"], e["subject"])
Response
{
"username": "drift4471",
"domain": "fmail.men",
"address": "drift4471@fmail.men",
"count": 1,
"emails": [
{
"id": 142,
"token": "3b09c05f-7f83-4eb3-b5e3-a71205191ed8",
"sender": "noreply@github.com",
"subject": "Your verification code is 942017",
"received_at": 1748490321034,
"text_len": 412,
"html_len": 1820
}
]
}
/v1/email/:token
Fetch a single email, including its body (plain text and HTML). Use the token from the inbox listing.
Response
{
"token": "3b09c05f-7f83-4eb3-b5e3-a71205191ed8",
"recipient": "drift4471",
"domain": "fmail.men",
"sender": "noreply@github.com",
"subject": "Your verification code is 942017",
"body_text": "Hi,\n\nYour verification code is 942017...",
"body_html": "<p>Hi,</p>...",
"received_at": 1748490321034
}
/v1/email/:token
Delete a single email. There's no undo.
curl -X DELETE https://fmail.men/v1/email/3b09c05f-7f83-4eb3-b5e3-a71205191ed8
await fetch("https://fmail.men/v1/email/3b09c05f-7f83-4eb3-b5e3-a71205191ed8", { method: "DELETE" });
requests.delete("https://fmail.men/v1/email/3b09c05f-7f83-4eb3-b5e3-a71205191ed8")
Recipe · polling for new mail
The cleanest way to wait for a verification code: poll /inbox every 5 seconds, pass since so you only get fresh mail, stop when you've got what you need.
async function waitForMail(address, { timeoutMs = 60_000, interval = 5_000 } = {}) {
const [username, domain] = address.split("@");
const start = Date.now();
let since = start;
while (Date.now() - start < timeoutMs) {
const url = `https://fmail.men/v1/inbox/${username}?domain=${domain}&since=${since}`;
const { emails } = await fetch(url).then(r => r.json());
if (emails.length) return emails[0];
await new Promise(r => setTimeout(r, interval));
}
throw new Error("timeout waiting for mail");
}
const first = await waitForMail("drift4471@fmail.men");
console.log("Got:", first.subject);
import time, requests
def wait_for_mail(address, timeout=60, interval=5):
user, domain = address.split("@")
start = time.time()
since = int(start * 1000)
while time.time() - start < timeout:
r = requests.get(f"https://fmail.men/v1/inbox/{user}",
params={"domain": domain, "since": since})
emails = r.json()["emails"]
if emails:
return emails[0]
time.sleep(interval)
raise TimeoutError("waiting for mail")
first = wait_for_mail("drift4471@fmail.men")
print("Got:", first["subject"])
Recipe · auto-extracting OTP codes
Combine polling with a regex to pluck a 4–8 digit code straight out of the latest message.
const OTP = /\b(\d{4,8})\b/;
async function fetchOTP(address) {
const mail = await waitForMail(address);
const full = await fetch(`https://fmail.men/v1/email/${mail.token}`).then(r => r.json());
const m = (full.subject + "\n" + (full.body_text || "")).match(OTP);
return m && m[1];
}
const code = await fetchOTP("drift4471@fmail.men");
console.log("OTP:", code);
import re
def fetch_otp(address):
mail = wait_for_mail(address)
full = requests.get(f"https://fmail.men/v1/email/{mail['token']}").json()
blob = (full.get("subject", "") + "\n" + (full.get("body_text") or ""))
m = re.search(r"\b(\d{4,8})\b", blob)
return m.group(1) if m else None
code = fetch_otp("drift4471@fmail.men")
print("OTP:", code)
Found a bug, want a feature, or need higher limits? Drop a line to owner@exolinker.com.