Getting Started
The fake-email.pro public API lets you create disposable mailboxes and read their incoming messages over HTTPS. This page walks you from zero to a working end-to-end request.
1. Get an API key
Open Settings → API keys and create a key. Copy the full token shown in the reveal screen – it starts with fep_ and is shown only once. The examples below read it from FEP_KEY: export FEP_KEY=fep_...
2. Authenticate every request
Every endpoint expects a Bearer token in the Authorization header:
Authorization: Bearer fep_...Treat the key like a password. Never embed it in a client-side bundle – call the API from a server you control.
This is a server-to-server API: CORS is off on api.fake-email.pro and no browser origin is allowed, so a fetch straight from a web page will not reach it. That is deliberate – it is also the only thing standing between a key in a bundle and anyone who opens devtools.
3. End-to-end example
Create a mailbox, send an email to it, wait for the email. The mailbox comes back with 201 Created and an address you use in the next calls; the shell version keeps it with jq.
ADDRESS=$(curl -s -X POST https://api.fake-email.pro/v1/mailboxes \
-H "Authorization: Bearer $FEP_KEY" | jq -r .address)
echo "$ADDRESS"curl -s -G "https://api.fake-email.pro/v1/mailboxes/$ADDRESS/messages/wait" \
-H "Authorization: Bearer $FEP_KEY" \
--data-urlencode 'timeout=60'curl -s "https://api.fake-email.pro/v1/mailboxes/$ADDRESS/messages?page=0&size=20" \
-H "Authorization: Bearer $FEP_KEY"The create call answers:
HTTP/1.1 201 Created
Content-Type: application/json
Date: Thu, 22 Oct 2026 09:00:00 GMT
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1792659660
{
"address": "anna.smith.42@radiant-flow.org",
"activeSince": "2026-10-22T09:00:00.481Z",
"expiresAt": "2026-10-23T09:00:00.481Z",
"storageUsedBytes": 0,
"messagesCount": 0
} And the wait, once the email is in, answers with the message in short form – the same form an item of the message list has. code and confirmLink are the best one-time code and confirmation link found in it, null when there is none; the bodies, attachments and every candidate are in GET /v1/mailboxes/{address}/messages/{id}.
{
"id": "5d2e8a10-7b3c-4f1e-8a2d-9c4b6e1f3a57",
"mailboxAddress": "anna.smith.42@radiant-flow.org",
"createdAt": "2026-10-22T09:06:40.102Z",
"sizeBytes": 9120,
"from": { "name": "Acme", "address": "no-reply@acme.example" },
"subject": "Your verification code",
"bodyState": "STORED",
"attachmentsCount": 0,
"code": "048213",
"confirmLink": "https://acme.example/verify?t=3f9a"
} Times are RFC 3339 in UTC, always with milliseconds. A mailbox accepts mail for 24 hours and cannot be renewed; once expiresAt passes, it and its messages answer 404 and are deleted soon after.
4. Every endpoint
Twelve operations on nine paths – that is the whole API. Request and response schemas for each of them live in the Reference. Lists take page (from 0) and size (1–50, 20 by default) and answer { content, page, size, totalPages, totalElements }; a value out of range is a 400.
| Endpoint | What it does |
|---|---|
POST /v1/mailboxes | Create a mailbox: 201 with it. Optional body { localPart?, domain? }, domain one of GET /v1/domains; the only call that spends monthly quota. |
GET /v1/mailboxes | List your active API mailboxes, newest first, a page at a time (?page=, ?size=). |
GET /v1/mailboxes/{address} | One mailbox: when it expires, storage used, message count. |
DELETE /v1/mailboxes/{address} | Delete a mailbox and its messages. 204, no body. The address stays taken forever. |
GET /v1/mailboxes/{address}/messages | A page of messages in short form, newest first (?page= from 0, ?size= 1–50, 20 by default). |
GET /v1/mailboxes/{address}/messages/wait | Long poll: holds the request up to ?timeout= seconds (1–60) and answers with the earliest message after ?after= that matches ?from= and ?subject=, or 204 if none came. |
GET /v1/mailboxes/{address}/messages/{id} | One message whole: html, text, attachments and every code and link found in it. |
DELETE /v1/mailboxes/{address}/messages/{id} | Delete a message. 204, no body. |
GET /v1/mailboxes/{address}/messages/{id}/raw | The message source as an .eml file (message/rfc822), also for mail we could not parse. |
GET /v1/mailboxes/{address}/messages/{id}/attachments/{attachmentId} | One attachment's bytes, with its declared Content-Type and file name. |
GET /v1/domains | Domains your plan can create mailboxes on, each with its type; PREMIUM ones only for plans that allow them. |
GET /v1/limits | Your monthly quota with what is spent and when it resets, requests per minute, waits at once, storage and the mailbox lifetime. Free: does not spend quota. |
5. Know your budget: GET /v1/limits
GET /v1/limits returns everything that caps you, with your plan's live numbers – the monthly mailbox quota with what you have spent of it and when it resets, requests per minute, waits held open at once, the storage your account's messages use and how long a mailbox lives. It is free: it does not spend quota, only one request of the per-minute limit, so ask it before a run rather than guessing.
curl -s https://api.fake-email.pro/v1/limits \
-H "Authorization: Bearer $FEP_KEY"{
"quota": { "limit": 20, "used": 3, "resetsAt": "2026-11-01T00:00:00.000Z" },
"requestsPerMinute": 60,
"concurrentWaits": 10,
"storage": { "usedBytes": 18422, "limitBytes": 52428800 },
"mailboxTtlSeconds": 86400
}The numbers depend on your plan and can change without a release, so read them here rather than hard-coding them; Limits explains each of them.
The per-minute limit, on every response
Every response the rate limiter saw – a success or an error such as 400, 402, 404, 409 or 429 – carries X-RateLimit-Limit (your requests per minute), X-RateLimit-Remaining (what is left in the current minute) and X-RateLimit-Reset (Unix seconds: when the window ends). The window is one fixed minute per user, shared by all your keys. The monthly quota is never in a header: it lives in GET /v1/limits and in the body of a 402.
An unknown path, a method the path does not have, a 401 and a 403 for a plan without API access come before the limiter and carry none. If the limiter itself is briefly unavailable, responses come without them too – read a missing header as unknown, not as zero.
6. Wait for emails, one after another
There are no webhooks and no streaming on this API, and polling the list of messages is the slow way: GET /v1/mailboxes/{address}/messages/wait answers the moment an email is in and counts as one request however long it waits (the Reference has its parameters).
- Without
afterit answers with the earliest matching email of the mailbox – one that came before the call included, so nothing sent between creating the mailbox and waiting is lost. The first wait on a fresh mailbox goes without it. after=<id>answers with the next email after that one. Pass theidof the email you just got and you walk the mailbox one email at a time, never skipping one.after=<time>(RFC 3339, such as2026-10-22T09:00:00Z) answers with an email received strictly after that moment by the server's clock – take it from theDateheader of a response, not from your machine and not from acreatedAtyou were shown.fromandsubjectnarrow it down by a case-insensitive substring;204with no body means the timeout passed without one – ask again.
curl -s -G "https://api.fake-email.pro/v1/mailboxes/$ADDRESS/messages/wait" \
-H "Authorization: Bearer $FEP_KEY" \
--data-urlencode 'timeout=60' \
--data-urlencode "after=$MESSAGE_ID"7. Errors
Every failure is JSON – { "error": "<code>", "message": "<phrase>" }. Switch on error, not on the status alone; message is a fixed English phrase for whoever reads the log. Besides the codes below, any call can meet 405 method_not_allowed, 413 payload_too_large or 415 unsupported_media_type (a body that is not JSON), and 503 with Retry-After when the service is momentarily overloaded.
| Status | Code | When |
|---|---|---|
| 400 | bad_request | A parameter of the wrong type or out of range (size=500, timeout=0), unreadable JSON, localPart without domain or a malformed local part. |
| 401 | unauthorized | No Authorization header, or a key that is unknown, revoked or expired. Any endpoint. |
| 402 | quota_exhausted | Monthly mailbox quota spent. POST /v1/mailboxes only – reads never spend it. |
| 403 | plan_forbidden | Your plan has no API access, or it does not allow a custom localPart. |
| 404 | not_found | No such active mailbox, message or attachment of yours – an expired mailbox included; or a domain your plan cannot use. |
| 409 | address_taken | The requested address exists already – forever, deleted ones included. |
| 429 | rate_limit_exceeded | Requests per minute spent. Any endpoint; Retry-After says when to come back. |
| 429 | too_many_waits | Too many waits of yours held open at once. GET …/messages/wait only, with Retry-After: 1. |
402 – the monthly quota is spent
Only POST /v1/mailboxes can return it. Next to the envelope the body carries your quota – limit, used and resetsAt, the first day of the next month at 00:00 UTC – and upgradeUrl, the plans page on /pricing. There is no Retry-After – waiting a minute does not bring the quota back. A create that fails for any reason spends no quota.
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 38
X-RateLimit-Reset: 1792659660
{
"error": "quota_exhausted",
"message": "monthly mailbox quota exhausted",
"limit": 20,
"used": 20,
"resetsAt": "2026-11-01T00:00:00.000Z",
"upgradeUrl": "https://fake-email.pro/pricing"
}429 – too many requests this minute
Requests are metered per minute and per user, not per key: every /v1 call counts, reads included. Sleep for Retry-After seconds and retry; your number is requestsPerMinute in GET /v1/limits, and Limits has the rest.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 17
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1792659660
{"error":"rate_limit_exceeded","message":"rate limit exceeded"}8. What's next
- Browse every endpoint and schema in the Reference.
- Plan around your quota and per-minute budget – see Limits.
- Mailboxes minted here are a pool of their own: they do not show up in the fake-email.pro web inbox, and mailboxes you create there are not visible to this API.