Back to home

    Open API

    Create disposable mailboxes and read the mail they receive, over HTTP. Built for automated sign-up tests and verification-mail flows.

    https://snaper24.com/api/v1

    Download the OpenAPI spec (JSON, for client generators and import into API tools).

    Authentication

    Every request carries an API key in the X-Api-Key header. Keys look like sk_<env>_<keyId>_<secret>.

    Create one on your API keys page. The secret is shown once, at creation — only a hash is stored, so it cannot be recovered afterwards. An account may hold three keys.

    curl https://snaper24.com/api/v1/open/domains \
      -H "X-Api-Key: sk_prod_a1b2c3_..."

    Scopes

    A key carries one or more scopes, and each endpoint needs a specific one. A key without it gets 403 INSUFFICIENT_SCOPE with the required scope in error.details. Scopes cannot be added to an existing key — issue a new one.

    • mailbox:read — list mailboxes and domains, read one mailbox
    • mailbox:write — create a mailbox, extend its TTL
    • mailbox:delete — delete a mailbox
    • email:read — poll and read mail, download attachments
    • email:write — mark mail as read

    Rate limits

    60 requests per minute per key. Responses that reach the per-key limiter — every authenticated request, including its own 429 — carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Pace against Remaining rather than against the clock. Refusals decided before your key is read — 401, and the per-address 429 below — deliberately carry none; use Retry-After there instead.

    The window is a counter that starts with its first request, not a wall-clock minute. One request per second fits exactly, with no margin: polling even slightly faster spends the budget before the window closes and the rest of it is refused. If you poll on a timer, leave headroom.

    A refusal is 429 RATE_LIMIT_EXCEEDED with a Retry-After header. A shared source address has an additional ceiling of 300 requests per minute across all keys, which answers IP_RATE_LIMIT_EXCEEDED.

    The wait is in the Retry-After header only — the response body carries no retry field.

    If rate limiting itself is unavailable the request is refused with 503 SERVICE_UNAVAILABLE rather than allowed through. Treat it as transient and retry with backoff.

    Response envelope

    Every response uses the same envelope.

    {
      "success": true,
      "data": { ... },
      "timestamp": "2026-10-10T12:00:00Z"
    }
    
    {
      "success": false,
      "error": {
        "code": "INSUFFICIENT_SCOPE",
        "message": "This API key does not carry the required scope",
        "details": "mailbox:write"
      },
      "timestamp": "2026-10-10T12:00:00Z"
    }

    Branch on error.code, not on the status code. 403 means two different things — a key that needs a different scope, and an account at its mailbox quota — and the remedies are opposite.

    Polling for new mail

    GET /open/mailboxes/{mailboxId}/emails returns an oldest-first page and a next_cursor. Pass that cursor back to receive only what arrived since. A next_cursor of null means you are at the end of the timeline — keep the last non-null cursor and poll with it.

    Rows omit has_attachments on purpose: deriving it would mean one extra query per row on an endpoint built to be polled, and a field that always said false would be a lie inside a frozen contract. Fetch a single email to see its attachments.

    # 1. Create a mailbox
    curl -X POST https://snaper24.com/api/v1/open/mailboxes \
      -H "X-Api-Key: $SNAPER24_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"ttl": 600}'
    
    # 2. Poll it for new mail, oldest first
    curl "https://snaper24.com/api/v1/open/mailboxes/$MAILBOX_ID/emails?limit=50" \
      -H "X-Api-Key: $SNAPER24_API_KEY"
    
    # 3. Pass the previous page's next_cursor to get only what is new
    curl "https://snaper24.com/api/v1/open/mailboxes/$MAILBOX_ID/emails?cursor=$NEXT_CURSOR" \
      -H "X-Api-Key: $SNAPER24_API_KEY"

    Endpoints

    GET/api/v1/open/domains
    mailbox:read

    List available domains

    Returns the domains a mailbox may be created on.

    Responses

    200
    Available domains
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:read scope
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    GET/api/v1/open/mailboxes
    mailbox:read

    List the key's mailboxes

    Returns the mailboxes owned by the account the API key belongs to, newest first.

    Parameters

    NameInTypeDescription
    limitqueryintegerRows per page (default 20, max 100)
    offsetqueryintegerRows to skip (default 0)

    Responses

    200
    The key account's mailboxes
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:read scope
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    POST/api/v1/open/mailboxes
    mailbox:write

    Create a mailbox

    Creates a mailbox owned by the account the API key belongs to.

    Parameters

    NameInTypeDescription
    request*bodyobjectMailbox creation request

    Responses

    201
    Mailbox created successfully
    400
    Invalid request: bad TTL, bad custom address, a reserved address, or an unknown domain
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:write scope, or the account mailbox quota is exhausted
    409
    Address already exists
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    GET/api/v1/open/mailboxes/{mailboxId}
    mailbox:read

    Get a mailbox

    Fetch one mailbox owned by the API key's account, including its email count.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)

    Responses

    200
    Mailbox information
    400
    Invalid mailbox ID
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:read scope
    404
    Mailbox not found, or not owned by this key's account
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    DELETE/api/v1/open/mailboxes/{mailboxId}
    mailbox:delete

    Delete a mailbox

    Delete a mailbox owned by the API key's account, and all its emails.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)

    Responses

    204
    Mailbox deleted successfully
    400
    Invalid mailbox ID
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:delete scope
    404
    Mailbox not found, or not owned by this key's account
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    GET/api/v1/open/mailboxes/{mailboxId}/emails
    email:read

    Poll a mailbox for new emails

    Keyset-paginated, oldest-first page of emails received after the given cursor. There is no cookie-path equivalent of this route.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)
    cursorquerystringOpaque cursor from a previous page's next_cursor
    limitqueryintegerPage size (default 50, max 200)

    Responses

    200
    Oldest-first page; next_cursor is null at the end of the timeline
    400
    Invalid mailbox ID, or the cursor could not be read
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the email:read scope
    404
    Mailbox not found, or not owned by this key's account
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    GET/api/v1/open/mailboxes/{mailboxId}/emails/{emailId}
    email:read

    Get one email

    Fetch a single email's full detail, including body, from a mailbox owned by the API key's account.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)
    emailId*pathstringEmail ID (UUID)

    Responses

    200
    Email detail
    400
    Invalid mailbox or email ID
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the email:read scope
    404
    Mailbox not found or not owned by this key's account, or email not found in the mailbox
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable
    PATCH/api/v1/open/mailboxes/{mailboxId}/emails/{emailId}
    email:write

    Update an email's read state

    Partial update of an email's mutable flags, in a mailbox owned by the API key's account.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)
    emailId*pathstringEmail ID (UUID)
    request*bodyobjectFields to update

    Responses

    200
    Updated email
    400
    Invalid mailbox or email ID, or invalid request body
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the email:write scope
    404
    Mailbox not found or not owned by this key's account, or email not found in the mailbox
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Failed to apply or re-fetch the update
    503
    Rate limiting temporarily unavailable
    GET/api/v1/open/mailboxes/{mailboxId}/emails/{emailId}/attachments/{attachmentId}
    email:read

    Download an email attachment

    Streams the attachment's bytes, proxied from the storage backend (Mailpit), from a mailbox owned by the API key's account.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)
    emailId*pathstringEmail ID (UUID)
    attachmentId*pathstringAttachment ID (UUID)

    Responses

    200
    Attachment bytes, with the sender's declared Content-Type
    400
    Invalid mailbox, email or attachment ID
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the email:read scope
    404
    Mailbox, email or attachment not found, or mailbox not owned by this key's account
    410
    Mailbox has expired, or this attachment is no longer available
    429
    Per-key or per-IP rate limit exceeded
    501
    Attachment downloads are not available for this message (unknown storage backend)
    502
    Could not retrieve the attachment, please try again (upstream fetch failed)
    503
    Rate limiting temporarily unavailable
    PATCH/api/v1/open/mailboxes/{mailboxId}/extend
    mailbox:write

    Extend a mailbox's TTL

    Extend the time-to-live of a mailbox owned by the API key's account.

    Parameters

    NameInTypeDescription
    mailboxId*pathstringMailbox ID (UUID)
    request*bodyobjectTTL extension request, 60-3600 seconds

    Responses

    200
    TTL extended successfully
    400
    Invalid mailbox ID, invalid request body, or TTL value out of range
    401
    Missing, malformed, revoked or expired API key
    403
    Key lacks the mailbox:write scope
    404
    Mailbox not found, or not owned by this key's account
    410
    Mailbox has expired
    429
    Per-key or per-IP rate limit exceeded
    500
    Internal server error
    503
    Rate limiting temporarily unavailable

    Error reference

    CodeStatusMeaningWhat to do
    UNAUTHORIZED401Missing or malformed X-Api-Key, or the account is disabled.Check the header. Do not retry unchanged.
    KEY_REVOKED401The key was revoked.Issue a new key.
    KEY_EXPIRED401The key passed its expiry.Issue a new key.
    INSUFFICIENT_SCOPE403The key does not carry the scope this endpoint needs. The required scope is in error.details.Issue a key with that scope. Retrying will not help.
    QUOTA_EXCEEDED403The account is at its active-mailbox limit.Delete a mailbox or wait for one to expire. Note this shares 403 with INSUFFICIENT_SCOPE — branch on error.code.
    INVALID_MAILBOX_ID400The mailbox id is not a UUID.Fix the request.
    INVALID_CURSOR400The cursor was not produced by this endpoint.Drop the cursor and start a fresh page.
    MAILBOX_NOT_FOUND404No such mailbox, or it belongs to another account.The two cases are deliberately indistinguishable.
    MAILBOX_EXPIRED410The mailbox reached its TTL and is gone.Create a new one. Do not retry.
    EMAIL_NOT_FOUND404No such email in this mailbox.It may have been evicted by the retention limit.
    ADDRESS_EXISTS409The requested address is taken.Choose another, or omit it and let the service pick.
    RATE_LIMIT_EXCEEDED429This key used its 60 requests for the current window.Wait the number of seconds in the Retry-After header.
    IP_RATE_LIMIT_EXCEEDED429This source address used its 300 requests for the window, across all keys.Wait Retry-After seconds.
    SERVICE_UNAVAILABLE503Rate limiting is temporarily unavailable. The request was refused rather than waved through.Retry with backoff. This is transient.
    ATTACHMENT_FETCH_FAILED502Upstream storage did not return the attachment.Retry with backoff.
    ATTACHMENT_STORAGE_UNAVAILABLE501This build cannot read the backend the attachment names.Report it. Retrying will not help.