DNS-only email security posture analysis. Checks 12 categories — SPF, DKIM, DMARC, MTA-STS, TLS-RPT, DANE, DNSSEC, BIMI, FCrDNS, DNSBL, MX, and cross-validation — and streams results as Server-Sent Events with an aggregate A–F grade.
See also: interactive API reference (auto-generated OpenAPI)
Endpoints
GET /inspect/{domain}
Inspect a domain's email security configuration. Returns an SSE stream.
curl -N https://email.netray.info/inspect/example.com
POST /inspect
Same inspection via POST body. Omit dkim_selectors to use default provider-mapped selector lookup.
curl -N -X POST https://email.netray.info/inspect \
-H 'Content-Type: application/json' \
-d '{"domain": "example.com"}'
With explicit DKIM selectors:
curl -N -X POST https://email.netray.info/inspect \
-H 'Content-Type: application/json' \
-d '{"domain": "example.com", "dkim_selectors": ["selector1", "google"]}'
Response Format (SSE)
Results stream as Server-Sent Events. Each event has a data field
containing a JSON object. Three event types arrive in sequence:
category events
One per check category (up to 11), streamed as each check completes:
data: {"type": "category", "name": "spf", "verdict": "pass", "checks": [
{"name": "spf_record_exists", "status": "pass", "message": "SPF record found"},
{"name": "spf_mechanism_count", "status": "pass", "message": "6 mechanisms (under limit of 10)"},
...
]}
cross_validation event
Arrives after all categories, containing cross-category consistency checks:
data: {"type": "cross_validation", "checks": [
{"name": "dmarc_alignment", "status": "pass", "message": "DMARC alignment matches SPF and DKIM"},
...
]}
summary event
Final event with the aggregate grade:
data: {"type": "summary", "grade": "B", "categories": {
"mx": "pass", "spf": "pass", "dkim": "warn", "dmarc": "pass",
"mta_sts": "fail", "tls_rpt": "fail", "dane": "skip",
"dnssec": "pass", "bimi": "skip", "fcrdns": "pass", "dnsbl": "pass"
}}
Check Categories
| Category | What It Checks |
|---|---|
mx | MX records exist and are valid hostnames |
spf | SPF record syntax, mechanism count, lookup limits |
dkim | DKIM key records for known and specified selectors |
dmarc | DMARC policy, alignment mode, reporting URIs |
mta_sts | MTA-STS DNS record and policy file |
tls_rpt | TLS-RPT DNS record for SMTP TLS reporting |
dane | DANE/TLSA records for MX hosts |
dnssec | DNSSEC validation for the domain |
bimi | BIMI DNS record and logo URL |
fcrdns | Forward-confirmed reverse DNS for MX IPs |
dnsbl | MX IPs checked against DNS blocklists |
cross_validation | Cross-category consistency (DMARC alignment, etc.) |
Grade Values
The aggregate grade reflects overall email security posture:
| Grade | Meaning |
|---|---|
| A | Excellent — all critical checks pass |
| B | Good — minor issues or missing optional records |
| C | Fair — some important checks warn or fail |
| D | Poor — significant security gaps |
| F | Failing — critical authentication missing |
Useful One-Liners
# Stream all results (SSE)
curl -N https://email.netray.info/inspect/example.com
# Get just the summary grade (wait for stream to complete)
curl -N -s https://email.netray.info/inspect/example.com \
| grep '"type":"summary"' | sed 's/^data: //' | jq '.grade'
# CI gate: check email security for a domain
curl -N -s https://email.netray.info/inspect/example.com \
| grep '"type":"summary"' | sed 's/^data: //' \
| jq -e '.grade == "A" or .grade == "B"'
Rate Limits
Per-IP GCRA rate limiting. Each inspection triggers multiple DNS lookups across all 12 check categories, so the effective cost per request is high.
When rate-limited, you receive a 429 response with a
Retry-After header.
Errors
{
"error": {
"code": "bad_request",
"message": "Missing required parameter: domain"
}
}
Common error codes:
| Code | Meaning |
|---|---|
bad_request | Missing or invalid domain parameter |
rate_limited | Too many requests; check Retry-After |
internal_error | Server error; include X-Request-Id in reports |