Open beta: PRO is free until we launch payments. We'll email you 30 days before that.

Tell us how it went

The API is young and we are still shaping it. Something missing, confusing, or plainly wrong – we want to hear it.

Docs

API reference

The reference is rendered from the same OpenAPI document the API is built from and served next to it, so it can never be a version behind. Twelve operations on nine paths, every request and response schema, and the error bodies – plus a request console that takes your fep_ key and fires real calls from the page.

The page is open – no key needed to read it: https://api.fake-email.pro/docs. Download the OpenAPI document from it and point a client generator at it to get a typed SDK in your language – that is the route we recommend while no official SDKs are published.

Conventions

  • Time. RFC 3339 in UTC, always with milliseconds: 2026-10-22T18:01:02.123Z (.000Z when the fraction is zero). Headers such as Retry-After and X-RateLimit-Reset stay in seconds.
  • Shape. Field names are camelCase; a field whose value is null is present, an empty list is []; enums are UPPERCASE (bodyState: "STORED"), error codes lowercase (not_found).
  • Lists. page (from 0) and size (1–50, 20 by default); out of range is a 400, never clamped. The answer is { content, page, size, totalPages, totalElements }, newest first.
  • Errors. Every error is JSON { "error": "<code>", "message": "<phrase>" }. Switch on error; message is a fixed English phrase for the developer reading the log.

A time you are shown is not a cursor: the server keeps microseconds, the JSON shows milliseconds, so two messages can share a createdAt. To go on after a message, use its id.

Waiting for an email

GET /v1/mailboxes/{address}/messages/wait is a long poll: it holds the request open until a matching email is in and answers with it, instead of a loop over the list of messages. It spends nothing of the monthly quota and one request of the per-minute rate limit, however long it waits (Limits).

  • timeout – Whole seconds to hold the request open, 1–60; 30 when left out.
  • after – Where to start: the id of a message of this mailbox, or an RFC 3339 time by the server's clock. Only a message strictly after it counts; without it, every message of the mailbox does – one that came before the call included.
  • from – A substring of the sender address, case-insensitive.
  • subject – A substring of the subject, case-insensitive.
  • 200 – The earliest matching message, in the same short form the list of messages uses – id, from, subject, bodyState, code and confirmLink. Sent at once if it is already there, the moment it arrives otherwise. Bodies, attachments and every code and link: GET /v1/mailboxes/{address}/messages/{id}.
  • 204 – No matching message within timeout. No body; ask again.
  • 400 bad_request – timeout outside 1–60 or not a number, or an after that is neither a message id nor an RFC 3339 time.
  • 404 not_found – No such active mailbox of yours (it may have expired or been deleted while the request waited), or an after id this mailbox does not hold.
  • 429 too_many_waits – Too many waits of yours held open at once, with Retry-After: 1. The cap is concurrentWaits in GET /v1/limits; any wait that ends frees its slot.

To take a mailbox's emails one by one, pass the id of the last one you got as after: each wait then answers with the next email, and none is skipped – not even one that arrived between two requests or in the same millisecond. To wait only for mail sent from some moment on, pass a time from the server's clock – the Date header of the response that created the mailbox, say – not the createdAt of a message you already have. A + in an offset goes into the URL as %2B.

Downloads

Two operations answer with a file instead of JSON. Both are reads: they cost nothing against the monthly quota, only against the per-minute rate limit, and a 404 still comes back as the usual JSON error, not_found.

  • GET /v1/mailboxes/{address}/messages/{id}/rawThe message exactly as received, as an .eml file (message/rfc822) – headers, every part, every attachment. It is the only way to read a message whose bodyState is UNPARSED; an OVERFLOW message kept no source and answers 404.
  • GET /v1/mailboxes/{address}/messages/{id}/attachments/{attachmentId}The decoded bytes of one attachment, with the Content-Type the sender declared and the real file name in Content-Disposition. Take the id from attachments of the message; an UNPARSED or OVERFLOW message has none.