API
Get a test address, send a mail to it, read the report as JSON. Three requests, the same flow as the browser.
Authentication
Send the X-API-Key header on every request. You create a key on your
account page and it is shown to you once.
X-API-Key: mt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Without a key the request counts as anonymous and the per IP daily limit applies.
Step 1 — get a test address
curl -X POST https://mailtesterbox.com/generate \ -H "X-API-Key: $MAILTESTER_KEY"
{
"address": "test-a1b2c3d4e5f6a7b8c9d0@mailtesterbox.com",
"expires_at": "2026-07-30T12:30:00+00:00",
"expires_in": 1800,
"limits": { "scope": "api_key", "limit": 25, "used": 3, "remaining": 22 }
}
The address lives 30 minutes and can be reused. Send a second mail to the same address and check again to get the newest report.
Step 2 — send a mail to that address
Send it from the server you want to test, exactly the way you normally send. We read the sending IP, the HELO name, the TLS version and every header from the connection itself.
Step 3 — ask for the report
curl "https://mailtesterbox.com/check/test-a1b2c3d4e5f6a7b8c9d0@mailtesterbox.com" \ -H "X-API-Key: $MAILTESTER_KEY"
This endpoint both queues the analysis and returns its state. Poll it every few seconds until the mail arrives.
| status | Meaning |
|---|---|
waiting | The address is live, no mail yet |
processing | The mail arrived, analysis is running |
analyzed | The report is ready, in the result field |
limit | Your daily allowance is used up |
expired | The address expired, get a new one |
error | Something went wrong during the analysis |
If you send more than one mail to the same address, pass ?after=<event_id> so the
mails you already saw are skipped and only the new one is awaited.
Endpoints
| Endpoint | Job |
|---|---|
POST /generate | New test address |
GET /check/{address} | Queue the analysis and poll for it |
GET /result/{address} | Newest report, read only, never touches the quota |
GET /limits | Remaining daily allowance |
GET /history | Your past reports |
GET /history/{id} | One past report in full |
For a schema level technical reference use the OpenAPI interface at /docs.
What the report contains
The result field carries the score out of 10, every deduction and all the checks.
{
"status": "analyzed",
"event_id": "6a6b12a4cc21db9abcd7ee74",
"result": {
"score": 9.2,
"title": "Excellent",
"issues": [
{ "code": "DMARC_POLICY_NONE", "points": -0.3, "severity": "low",
"title": "DMARC policy is set to none", "how_to_fix": "..." }
],
"checks": {
"spf": { "status": "ok", "result": "pass", "record": ["v=spf1 ip4:... -all"] },
"dkim": { "status": "ok", "verified": true, "selector": "default", "signing_domain": "..." },
"dmarc": { "status": "ok", "result": "pass", "policy": "none",
"spf_aligned": true, "dkim_aligned": true },
"rdns": { "success": true, "hostname": "mx.ornek.com", "matches": true },
"helo": { "value": "mx.ornek.com", "is_fqdn": true, "matches_rdns": true },
"blacklists": { "checked": 20, "results": { "zen.spamhaus.org": "not_listed" } },
"domain_blacklists": { "checked": 8, "listed": [] },
"content": { "has_plain": true, "has_html": true, "text_ratio": 0.42,
"link_hosts": ["ornek.com"], "shortened_links": [] },
"spamassassin": { "score": 0.1, "threshold": 5.0,
"rules": [ { "name": "TO_MALFORMED", "points": 0.1, "description": "..." } ] }
},
"connection": { "client_ip": "203.0.113.10", "helo": "mx.ornek.com",
"tls": true, "tls_protocol": "TLSv1.3" }
}
}
The points values inside issues are negative and their sum is taken off 10.
The code field is stable, so you can branch on it on your side.
Limits
| Scope | Analyses per day | Request rate |
|---|---|---|
| Anonymous (IP) | 5 | 10 / minute |
| Account (browser) | 25 | 10 / minute |
| API key | 25 | 120 / minute |
The daily allowance is charged when the analysis starts, not when the address is created, so
creating an address, switching language or reading /result is free. Over the limit you
get 429 with a Retry-After header.
Every one of these values is an environment variable if you run it on your own server. The project is open source: github.com/alialinx/mail-tester
Full example
#!/usr/bin/env bash
set -euo pipefail
KEY="$MAILTESTER_KEY"
BASE="https://mailtesterbox.com"
ADDRESS=$(curl -sS -X POST "$BASE/generate" -H "X-API-Key: $KEY" | jq -r .address)
echo "address: $ADDRESS"
swaks --to "$ADDRESS" --from "test@ornek.com" --server mail.ornek.com
for i in $(seq 1 60); do
BODY=$(curl -sS "$BASE/check/$ADDRESS" -H "X-API-Key: $KEY")
STATUS=$(echo "$BODY" | jq -r .status)
[ "$STATUS" = "analyzed" ] && { echo "$BODY" | jq '.result.score, .result.issues'; break; }
[ "$STATUS" = "expired" ] || [ "$STATUS" = "limit" ] && { echo "$BODY"; exit 1; }
sleep 3
done