Email Deliverability & DNS Health API
The Email Deliverability & DNS Health API returns a domain's email authentication and DNS posture as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary check endpoint returns an overall score and grade with a breakdown, plus per-record results for SPF, DKIM, DMARC, MX, BIMI, MTA-STS, TLS-RPT, DNSSEC and DANE. It is built for deliverability tools, security audits and onboarding checks that need a complete email-DNS health signal without running a dozen separate lookups. One ReefAPI key, one shared credit pool, the standard envelope.
How the 0–100 deliverability score is weighted
The score is not a black box: every response returns score.breakdown with each section's earned points, its maximum and a note explaining the number. Those maxima are fixed and shown below. If you only care about one dimension, call that section's standalone action instead of check — the weights tell you which ones the score barely moves for.
| Section | Max points | What earns them |
|---|---|---|
| spf | 22 | a valid SPF record, DNS lookups within the limit, and an all-qualifier that is not +all |
| dmarc | 22 | a published policy at _dmarc.<domain>; p=quarantine at pct=100 measured full marks |
| mx | 20 | at least one resolvable MX record |
| dkim | 16 | at least one selector with a usable public key |
| blacklist | 10 | no DNSBL listings across the domain's A and MX-host IPs |
| mta_sts | 4 | an _mta-sts TXT plus a fetchable policy file |
| dnssec | 3 | the zone is signed — resolver AD flag and a DS record at the parent |
| tls_rpt | 2 | a TLS-RPT record at _smtp._tls.<domain> |
| bimi | 1 | a BIMI record with a logo URL |
dane is returned but carries no points — it is reported as informational because almost no commercial mail platform publishes TLSA records, and scoring it would penalize domains for a decision their provider made.
Real request and response JSON
Captured from the indexed primary action, check, on .
{
"method": "POST",
"url": "https://api.reefapi.com/email-health/v1/check",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"domain": "google.com"
}
}{
"ok": true,
"meta": {
"api": "email-health",
"endpoint": "check",
"mode": "live",
"latency_ms": 7173.5,
"record_count": 3,
"bytes": 72303,
"cache_hit": false,
"method": "doh:cloudflare+google",
"score": 80,
"grade": "B"
},
"data": {
"domain": "google.com",
"score": {
"score": 80,
"max": 100,
"grade": "B",
"breakdown": {
"mx": {
"score": 20,
"max": 20,
"note": "1 MX record(s) — Google Workspace"
},
"spf": {
"score": 22,
"max": 22,
"note": "valid SPF, 1/10 lookups, all=~"
},
"dmarc": {
"score": 22,
"max": 22,
"note": "p=reject pct=100"
},
"dkim": {
"score": 0,
"max": 16,
"note": "no DKIM for tested selector(s)"
},
"blacklist": {
"score": 10,
"max": 10,
"note": "clean across 15 DNSBLs"
},
"mta_sts": {
"score": 4,
"max": 4,
"note": "enforce"
},
"dnssec": {
"score": 0,
"max": 3,
"note": "unsigned"
},
"tls_rpt": {
"score": 2,
"max": 2,
"note": "present"
},
"bimi": {
"score": 0,
"max": 1,
"note": "absent"
}
}
},
"summary": {
"has_spf": true,
"spf_valid": true,
"has_dmarc": true,
"dmarc_enforced": true,
"has_dkim": false,
"has_mx": true,
"mx_provider": "Google Workspace",
"has_bimi": false,
"mta_sts_enforced": true,
"dnssec_signed": false,
"has_tls_rpt": true,
"has_dane": false,
"blacklisted": false,
"blacklist_listings": 0
},
"issues": [
"DKIM record(s) present but all have an empty p= (revoked) — no usable signing key",
"selector '20230601' has an empty p= (key revoked)",
"selector '20161025' has an empty p= (key revoked)"
],
"spf": {
"domain": "google.com",
"found": true,
"record": "v=spf1 include:_spf.google.com ~all",
"valid": true,
"dns_lookups": 1,
"void_lookups": 0,
"policy": "softfail (recommended)",
"mechanisms": [
"include:_spf.google.com",
"~all"
],
"includes": [
"_spf.google.com"
],
"all_qualifier": "~",
"issues": [],
"include_tree": [
{
"target": "_spf.google.com",
"record": "v=spf1 ip4:[redacted-phone]/16 ip4:[redacted-phone]/17 ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56 ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56 ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all",
"lookups": 0,
"void": 0
}
]
},
"dkim": {
"domain": "google.com",
"found": false,
"record_exists": true,
"wildcard_revoke": false,
"selectors_tested": [
"google",
"20230601",
"20161025"
],
"selectors_found": [],
"selectors_with_record": [
"20230601",
"20161025"
],
"records": [
{
"selector": "google",
"found": false,
"usable": false,
"name": "google._domainkey.google.com",
"record": null,
"_bytes": 509
},
{
"selector": "20230601",
"found": true,
"usable": false,
"name": "20230601._domainkey.google.com",
"record": "v=DKIM1; k=rsa; p=",
"_bytes": 236,
"key_type": "rsa",
"public_key_present": false,
"revoked": true,
"flags": null
},
{
"selector": "20161025",
"found": true,
"usable": false,
"name": "20161025._domainkey.google.com",
"record": "k=rsa; p=",
"_bytes": 227,
"key_type": "rsa",
"public_key_present": false,
"revoked": true,
"flags": null
}
],
"issues": [
"DKIM record(s) present but all have an empty p= (revoked) — no usable signing key",
"selector '20230601' has an empty p= (key revoked)",
"selector '20161025' has an empty p= (key revoked)"
]
},
"dmarc": {
"domain": "google.com",
"found": true,
"record": "v=DMARC1; p=reject; rua=mailto:[redacted-email]",
"valid": true,
"policy": "reject",
"subdomain_policy": "reject",
"pct": 100,
"rua": [
"mailto:[redacted-email]"
],
"ruf": [],
"adkim": "r",
"aspf": "r",
"fo": "0",
"issues": [],
"tags": {
"v": "DMARC1",
"p": "reject",
"rua": "mailto:[redacted-email]"
},
"enforced": true
},
"mx": {
"domain": "google.com",
"found": true,
"count": 1,
"records": [
{
"preference": 10,
"exchange": "smtp.google.com"
}
],
"hosts": [
"smtp.google.com"
],
"provider": "Google Workspace",
"issues": []
},
"bimi": {
"domain": "google.com",
"found": false,
"record": null,
"selector": "default",
"logo_url": null,
"vmc_url": null
},
"mta_sts": {
"domain": "google.com",
"found": true,
"record": "v=STSv1; id=20210803T010101;",
"id": "20210803T010101",
"policy": null,
"mode": "enforce",
"mx": [
"smtp.google.com",
"aspmx.l.google.com",
"*.aspmx.l.google.com"
],
"max_age": 86400,
"policy_fetched": true,
"issues": [],
"policy_url": "https://mta-sts.google.com/.well-known/mta-sts.txt",
"enforced": true
},
"tls_rpt": {
"domain": "google.com",
"found": true,
"record": "v=TLSRPTv1;rua=mailto:[redacted-email]",
"rua": [
"mailto:[redacted-email]"
]
},
"dnssec": {
"domain": "google.com",
"validated_ad_flag": false,
"has_ds_record": false,
"signed": false
},
"dane": {
"domain": "google.com",
"found": false,
"mx_hosts_checked": [
"smtp.google.com"
],
"hosts": [
{
"mx_host": "smtp.google.com",
"name": "_25._tcp.smtp.google.com",
"found": false,
"records": [],
"dnssec_validated": false
}
],
"record_count": 0,
"issues": []
},
"blacklist": {
"domain": "google.com",
"checked_ips": [
"[redacted-phone]",
"[redacted-phone]",
"[redacted-phone]"
],
"ptr_records": {
"142.251.163.100": "wv-in-f100.1e100.net",
"142.251.163.101": "wv-in-f101.1e100.net",
"142.251.163.102": "wv-in-f102.1e100.net",
"142.251.163.113": "wv-in-f113.1e100.net",
"142.251.163.138": "wv-in-f138.1e100.net",
"142.251.163.139": "wv-in-f139.1e100.net",
"172.253.115.26": "bg-in-f26.1e100.net",
"172.253.122.26": "bh-in-f26.1e100.net",
"172.253.139.26": "yuiadsk-in-f26.1e100.net",
"172.253.63.26": "bi-in-f26.1e100.net",
"172.253.63.27": "bi-in-f27.1e100.net"
},
"listings": [],
"listed_count": 0,
"clean": true,
"checks_run": 165,
"resolver_refused_count": 0,
"zones_checked": [
"bl.spamcop.net",
"b.barracudacentral.org",
"dnsbl-1.uceprotect.net"
],
"paid_key_zones": [
{
"zone": "zen.spamhaus.org",
"name": "[redacted-name]",
"reason": "returns [redacted-phone] on public resolvers — needs a Spamhaus DQS (Data Query Service) key"
},
{
"zone": "cbl.abuseat.org",
"name": "[redacted-name]",
"reason": "served via Spamhaus infra — same public-resolver refusal"
}
],
"issues": []
}
}
}What the Email Deliverability & DNS Health API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| check | Full email-health audit: SPF + DKIM + DMARC + MX + BIMI + MTA-STS + TLS-RPT + DNSSEC + blacklist, plus a 0-100 deliverability score and letter grade. | Platform and DevOps teams call check to get full email-health audit. | domain, selector, selectors, bimi_selector, skip_blacklist |
| spf | Parse + validate the SPF record: follow include:/redirect=, count the ≤10 DNS-lookup mechanisms, flag +all and void lookups, report the all-qualifier policy. | Security and supply-chain teams call spf to parse + validate the SPF record. | domain |
| dmarc | Parse the DMARC policy at _dmarc.<domain>: p/sp/pct/rua/ruf/adkim/aspf/fo + enforcement verdict. | Developer-tool builders call dmarc to parse the DMARC policy at _dmarc.<domain>. | domain |
| dkim | DKIM selector lookup (<selector>._domainkey.<domain>): key type, public-key presence, RSA key-bit estimate, revocation. Autoscans ~30 common selectors if none given. | AI-agent developers call dkim to get dKIM selector lookup (<selector>._domainkey.<domain>). | domain, selector, selectors |
| mx | MX records (sorted by preference) + a mail-provider guess from the exchange hostnames. | Platform and DevOps teams call mx to get mX records (sorted by preference) + a mail-provider guess from the exchange hostnames.. | domain |
| blacklist | DNSBL reputation: reverse the domain's A + MX-host IPs against live-proven public DNSBLs (SpamCop, Barracuda, UCEPROTECT, PSBL, Mailspike, GBUdb, DroneBL, s5h). Spamhaus/CBL are flagged as paid-key-required (public-resolver refused), not queried. | Security and supply-chain teams call blacklist to get dNSBL reputation. | domain, ips |
| mta_sts | MTA-STS: the _mta-sts.<domain> TXT (id) plus the fetched https://mta-sts.<domain>/.well-known/mta-sts.txt policy (mode, mx[], max_age). | Developer-tool builders call mta_sts to get mTA-STS. | domain |
| bimi | BIMI brand-logo record at <selector>._bimi.<domain>: the published logo (SVG) URL and the VMC (Verified Mark Certificate) URL, plus whether a VMC is present. Standalone lookup (MXToolbox SuperTool exposes BIMI on its own — no full audit needed). | AI-agent developers call bimi to get bIMI brand-logo record at <selector>._bimi.<domain>. | domain, selector |
| tls_rpt | SMTP TLS Reporting (RFC 8460) record at _smtp._tls.<domain>: the rua= report destination(s) that receive aggregate TLS-negotiation failure reports. Standalone lookup. | Platform and DevOps teams call tls_rpt to get sMTP TLS Reporting (RFC 8460) record at _smtp._tls.<domain>. | domain |
| dnssec | DNSSEC signing status: the DoH AD (Authenticated-Data) flag = the resolver cryptographically validated the answer, plus a DS record at the parent = the delegation is signed. Standalone lookup. | Security and supply-chain teams call dnssec to get dNSSEC signing status. | domain |
| dane | DANE for SMTP (RFC 7672): TLSA records at _25._tcp.<mx-host> for every MX host, with per-host DNSSEC validation (DANE is only effective on a signed TLSA RRset). Value-add: MXToolbox has NO TLSA/DANE lookup. | Developer-tool builders call dane to get dANE for SMTP (RFC 7672). | domain |
| batch | Run the full 'check' audit on up to 25 domains in one call (each with score + grade). | AI-agent developers call batch to get run the full 'check' audit on up to 25 domains in one call (each with score + grade).. | domains, skip_blacklist |
Call check from your stack
curl -X POST https://api.reefapi.com/email-health/v1/check \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"domain":"google.com"}'import requests
r = requests.post(
"https://api.reefapi.com/email-health/v1/check",
headers={"x-api-key": REEF_KEY},
json={
"domain": "google.com"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/email-health/v1/check", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"domain": "google.com"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.email-health.check with {"domain":"google.com"}.Who uses this API and why
- Deliverability tools call check to grade a sending domain's SPF, DKIM and DMARC before a campaign.
- Security audits use dmarc and mta_sts to confirm a domain's anti-spoofing posture.
- Onboarding flows use the overall score to warn customers about misconfigured email DNS.
Questions developers ask before integrating
What does a real-world score look like — is 100 the normal target?
No. A measured audit of github.com scored 86, grade B. It earned full marks on mx, spf and dmarc and 16/16 on dkim, then lost points on exactly the sections most large senders skip: mta_sts 0/4 (absent), dnssec 0/3 (unsigned), tls_rpt 0/2 (absent), bimi 0/1 (absent), and blacklist 6/10 for one listing. Treat the high-weight sections as pass/fail and the small ones as headroom.
Why does the SPF section report dns_lookups and void_lookups?
Because SPF's cost is capped at 10 DNS-resolving mechanisms, and blowing past it makes receivers fail the record entirely rather than soft-fail it. The parser follows every include: and redirect= and counts them for you. Measured on github.com: dns_lookups 10 — exactly at the limit — with void_lookups 0. Sitting at 10 means the next include your marketing team adds breaks authentication silently. void_lookups counts includes that resolve to nothing, which burn budget for no benefit.
How does DKIM lookup work if I do not know the selector?
DKIM has no discovery mechanism — a selector is an arbitrary label the sender chose — so with no selector given the check autoscans roughly 40 common ESP selectors and reports selectors_tested alongside selectors_found. Measured on github.com that found ten live: google, selector1, k1, k2, k3, s1, s2, zendesk1, zendesk2 and cm, which incidentally maps out which platforms send mail as that domain. Each record comes back with key_type, an RSA key_bits estimate (2048 in the measured case), public_key_present and revoked. If you know your selector, pass it and skip the scan.
Which blacklists are actually queried, and which are not?
The check resolves the domain's A and MX-host IPs, reverses them against public DNSBLs that answer public resolvers — SpamCop, Barracuda, UCEPROTECT, PSBL, Mailspike, GBUdb, DroneBL, SPFBL, s5h — and returns checked_ips, ptr_records, listings[] with the zone and its return codes, listed_count and a clean boolean. Spamhaus and CBL are deliberately not queried: they refuse public-resolver traffic and require a paid key, so they are reported in paid_key_zones rather than silently counted as clean. A measured github.com run made 75 individual checks and found one listing.
Why is a check call slow, and how do I speed it up?
A full check measured 9.7 seconds because the blacklist section is many IP-by-zone DNS queries — 75 of them in the measured run — while every other section is a handful of lookups. Pass skip_blacklist to drop that section, or call the standalone spf, dmarc, dkim, mx, bimi, mta_sts, tls_rpt, dnssec or dane actions when you only need one.
What is the difference between dmarc `policy` and `enforced`?
policy is the raw p= tag and enforced is the verdict after pct and subdomain policy are taken into account. Measured on github.com: policy 'quarantine', subdomain_policy 'reject', pct 100, enforced true. A domain publishing p=none, or p=reject at pct=10, has a DMARC record and is still not enforcing anything — which is why summary.dmarc_enforced exists as a separate boolean from summary.has_dmarc.
Can I pass an email address or a URL instead of a bare domain?
Yes. A full URL, a leading www., or a whole email address are all normalized to the registrable domain before any lookup runs, so [email protected] and https://www.example.co.uk/pricing both resolve to the same audit. The normalized value is echoed back as domain so you can confirm what was actually checked.
What is in summary versus the individual sections?
summary is a flat set of booleans meant for dashboards and alerting rules — has_spf, spf_valid, has_dmarc, dmarc_enforced, has_dkim, has_mx, mx_provider, has_bimi, mta_sts_enforced, dnssec_signed, has_tls_rpt, has_dane, blacklisted, blacklist_listings — while the per-section objects carry the raw records and parse trees. issues[] merges every section's complaints into one list, which is usually what you want to show a customer. mx_provider is a guess derived from the exchange hostnames (measured: 'Microsoft 365' for github.com), so treat it as a label, not a contract.
What is the Email Deliverability & DNS Health API?
Email Deliverability & DNS Health API is a ReefAPI endpoint group for email deliverability & dns health It returns live JSON through POST requests under /email-health/v1.
Is the Email Deliverability & DNS Health API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Email Deliverability & DNS Health calls use the same shared credit balance as every other ReefAPI engine.
Do I need an Email Deliverability & DNS Health login or account?
No login to Email Deliverability & DNS Health is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Email Deliverability & DNS Health data?
The page example is captured from a live check call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Email Deliverability & DNS Health API use?
Email Deliverability & DNS Health actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Email Deliverability & DNS Health from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call email-health actions with the same key, credit pool and JSON envelope used by normal REST requests.