fmail /api

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}"
  }
}
CodeStatusMeaning
invalid_username400Bad username format
not_found404Email id doesn't exist (or endpoint doesn't)
method_not_allowed405Used wrong HTTP method

Reference

Six endpoints. All return JSON. All support CORS.

GET /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}
GET /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"
}
GET /v1/random

Generate a random username on a chosen domain. Saves you from having to think one up.

QueryTypeDefaultNotes
domainstringfmail.menMust be one returned by /v1/domains

Response

{
  "username": "drift4471",
  "domain": "fmail.men",
  "address": "drift4471@fmail.men"
}
GET /v1/inbox/:username

List emails in an inbox, newest first. Email bodies aren't included — use /v1/email/:token to fetch the full content.

ParamTypeDefaultNotes
:usernamepathLocal part of the address
domainqueryfmail.menPick the domain
sincequery · msOnly emails received after this timestamp
limitquery · 1–5050Cap 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
    }
  ]
}
GET /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
}
DEL /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.