neighbours.sh

API Reference

Public REST API for reading mail from any inbox. No authentication, no rate limits, CORS open, JSON everywhere.

Contents

Overview

Endpoints

List available domains

GET/api/v1/config/domains
Returns every domain this server accepts mail for. Use this to populate your own UI or script your address generation.
curl https://neighbours.sh/api/v1/config/domains
const res = await fetch('https://neighbours.sh/api/v1/config/domains');
const { data } = await res.json();
console.log(data.domains); // ["tomboy.live", "tomboy.best", ...]
import requests

domains = requests.get("https://neighbours.sh/api/v1/config/domains").json()["data"]["domains"]
print(domains)
Response
{
  "success": true,
  "data": {
    "domains": [
      "tomboy.live",
      "tomboy.best",
      "diddybld.shop",
      "mogging.lol",
      "perimeter-x.com"
    ]
  }
}

List emails in an inbox

GET/api/v1/inbox/{address}
Returns summaries of all emails delivered to {address}. Optional query params ?subject= and ?from= filter the result server-side (case-insensitive substring match).
curl https://neighbours.sh/api/v1/inbox/alice@tomboy.live
# filter by subject:
curl 'https://neighbours.sh/api/v1/inbox/alice@tomboy.live?subject=verification'
const res = await fetch('https://neighbours.sh/api/v1/inbox/alice@tomboy.live?subject=verification');
const { data } = await res.json();
console.log(data); // array of mail summaries matching subject
import requests

res = requests.get("https://neighbours.sh/api/v1/inbox/alice@tomboy.live",
                   params={"subject": "verification"})
data = res.json()["data"]
print(data)  # summaries with "verification" in subject
Response
{
  "success": true,
  "data": [
    {
      "uid": 42,
      "from": "sender@example.com",
      "subject": "Verify your account",
      "date": "2026-04-14T17:05:00Z",
      "snippet": "Click the link to verify..."
    }
  ]
}

Get a single email

GET/api/v1/inbox/{address}/{uid}
Returns the full email including text, HTML, and attachment metadata.
curl https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42
const res = await fetch('https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42');
const { data } = await res.json();
console.log(data.subject, data.text);
import requests

mail = requests.get("https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42").json()["data"]
print(mail["subject"], mail["text"])
Response
{
  "success": true,
  "data": {
    "uid": 42,
    "from": "sender@example.com",
    "to": "alice@tomboy.live",
    "subject": "Your verification code",
    "date": "2026-04-14T17:05:00Z",
    "text": "Your code is 384921. Click https://example.com/verify?t=abc to continue.",
    "html": "

Your code is 384921.

", "codes": ["384921"], "urls": ["https://example.com/verify?t=abc"], "attachments": [ { "filename": "doc.pdf", "size": 82113, "checksum": "abc123" } ] } }

codes — auto-extracted OTP / verification codes (4–8 digit blocks near words like code, otp, verify, pin). Returns [] when nothing is detected.
urls — every http(s):// link found in the body, deduplicated (max 50). Useful for one-click access to verification links without parsing HTML.

Raw message source

GET/api/v1/inbox/{address}/{uid}/raw
Returns the complete RFC 822 source of the message.
curl https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/raw
const raw = await fetch('https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/raw')
  .then(r => r.text());
raw = requests.get("https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/raw").text

Download an attachment

GET/api/v1/inbox/{address}/{uid}/attachment/{checksum}
Returns the attachment binary with correct Content-Type.
curl -O https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/attachment/abc123
const blob = await fetch('https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/attachment/abc123')
  .then(r => r.blob());
r = requests.get("https://neighbours.sh/api/v1/inbox/alice@tomboy.live/42/attachment/abc123")
open("file.bin", "wb").write(r.content)

Delete one email

DELETE/api/v1/mail/inbox/{address}/{uid}
Permanently removes a single email by UID.
curl -X DELETE https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live/42
await fetch('https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live/42', {
  method: 'DELETE'
});
requests.delete("https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live/42")

Empty an inbox

DELETE/api/v1/mail/inbox/{address}?confirm=true
Deletes every email in the inbox. Requires confirm=true.
curl -X DELETE 'https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live?confirm=true'
await fetch('https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live?confirm=true', {
  method: 'DELETE'
});
requests.delete("https://neighbours.sh/api/v1/mail/inbox/alice@tomboy.live",
                params={"confirm": "true"})

Statistics

GET/api/v1/stats
Current email count, all-time total, and recent activity.
curl https://neighbours.sh/api/v1/stats
const { data } = await fetch('https://neighbours.sh/api/v1/stats').then(r => r.json());
console.log(data.allTimeTotal);
stats = requests.get("https://neighbours.sh/api/v1/stats").json()["data"]
print(stats["allTimeTotal"])

Health check

GET/api/v1/health
Returns 200 when the service is up.
curl https://neighbours.sh/api/v1/health
const health = await fetch('https://neighbours.sh/api/v1/health').then(r => r.json());
health = requests.get("https://neighbours.sh/api/v1/health").json()

Response format

Every JSON response follows this envelope:

// Success
{ "success": true, "data": ... }

// Error
{ "success": false, "error": "...", "code": "..." }

Error codes

CodeHTTPMeaning
VALIDATION_ERROR400Bad input (e.g. malformed address)
NOT_FOUND404Address or UID does not exist
CONFIRMATION_REQUIRED400Missing confirm=true on empty-inbox