API

Free. No keys, no accounts, no signup.


Base URL: https://spam.is/api. Rate limit on /inbox/*: 30 requests/minute/IP. /domains/*: 20 requests/hour/IP. That's it, no registration, no tiers, use it.

Address format everywhere below: lowercase letters, numbers, dots, underscores, hyphens, up to 64 characters. Local-part only, no @spam.is in the URL.

GET /api/inbox/:address

All messages currently stored for that address.

curl https://spam.is/api/inbox/testuser
{
  "address": "testuser@spam.is",
  "messages": [
    {
      "from": "someone@example.com",
      "subject": "hello",
      "text": "plain text body\n",
      "html": null,
      "date": "2026-08-03T18:40:00.000Z",
      "attachments": []
    }
  ]
}

400 if the address doesn't match the format above. An empty messages array isn't an error, it just means nothing has arrived yet, or the address never existed. There's no way to tell those apart, on purpose.

DELETE /api/inbox/:address

Deletes everything stored for that address. Immediate, no undo.

curl -X DELETE https://spam.is/api/inbox/testuser
{ "burned": "testuser@spam.is" }

GET / DELETE /api/inbox/:domain/:address

Same as above, for a verified custom domain instead of spam.is. 404 if the domain isn't registered and verified. For private custom domains, requires management key authentication. For GET: use x-management-key header or key query parameter. For DELETE: use x-management-key header or in request body. Public domains don't require authentication.

GET with query param:
curl "https://spam.is/api/inbox/example.com/testuser?key=YOUR_MANAGEMENT_KEY"

GET with header:
curl -H "x-management-key: YOUR_MANAGEMENT_KEY" https://spam.is/api/inbox/example.com/testuser

DELETE with header:
curl -X DELETE -H "x-management-key: YOUR_MANAGEMENT_KEY" https://spam.is/api/inbox/example.com/testuser

Custom Domains

Bring your own domain: point its MX at mail.spam.is, prove you control it with a DNS TXT record, and every address at it works like @spam.is. No account - the managementKey returned from POST /api/domains is required for verify and DELETE, and is shown exactly once. Full flow: domains.html.

POST /api/domains

curl -X POST https://spam.is/api/domains \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com"}'
{
  "domain": "example.com",
  "managementKey": "...",
  "status": "pending",
  "txtRecordName": "_spamis-verify.example.com",
  "txtRecordValue": "...",
  "mxRecord": "mail.spam.is",
  "mxPriority": 10
}

409 if the domain is already registered.

POST /api/domains/:domain/verify

Checks the TXT record and, on a match, starts accepting mail for the domain.

curl -X POST https://spam.is/api/domains/example.com/verify \
  -H "Content-Type: application/json" \
  -d '{"managementKey": "..."}'
{ "verified": true }

GET /api/domains/:domain

Public status check, no key needed.

{ "domain": "example.com", "verified": true, "createdAt": "...", "verifiedAt": "..." }

DELETE /api/domains/:domain

Stops accepting mail for the domain. Requires the managementKey.

curl -X DELETE https://spam.is/api/domains/example.com \
  -H "Content-Type: application/json" \
  -d '{"managementKey": "..."}'

GET /api/domains/public

Get the list of public domains available for instant use without registration. These domains are pre-configured and verified by spam.is.

curl https://spam.is/api/domains/public
{
  "domains": [
    { "name": "example1.com", "enabled": true },
    { "name": "example2.com", "enabled": true }
  ]
}

Public domains can be used immediately without any registration or verification. You can check inboxes on public domains just like any other custom domain, no management key needed.

POST /api/domains/:domain/reset

Lost the managementKey? Current DNS control is exactly the proof we accepted the first time, so it's enough to issue a new one - no old key needed. Returns a fresh token to publish as the TXT record, replacing the old value. Doesn't affect mail already being accepted for the domain.

curl -X POST https://spam.is/api/domains/example.com/reset
{ "domain": "example.com", "txtRecordName": "_spamis-verify.example.com", "txtRecordValue": "..." }

POST /api/domains/:domain/reset/confirm

Checks the new TXT record and, on a match, issues a new managementKey.

curl -X POST https://spam.is/api/domains/example.com/reset/confirm
{ "confirmed": true, "managementKey": "...", "verified": true }

GET /api/stats

Global counters. Not tied to any address, not affected by burns.

curl https://spam.is/api/stats
{
  "total_messages": 4213,
  "unique_addresses": 1897,
  "unique_addresses_approx": true
}

total_messages is exact. unique_addresses is a HyperLogLog estimate (±0.81% typical error), there's no stored list of every address ever used behind it, just a counter.

GET /api/health

curl https://spam.is/api/health
{ "ok": true }

Attachments come back base64-encoded (filename, contentType, size, content), completely unfiltered, nothing is scanned. If you're building something that renders html or opens attachments, treat all of it as hostile input. See the Terms of Service for what you can't use this for.