GET/api/v2/dmarc

DMARC check API

The DMARC policy receivers apply to a domain: the record found (or inherited), every tag's effective value, where reports go and whether those addresses accept them, and how strong the policy is.

GEThttps://dnsrobot.netBase URL/api/v2Version/dmarcEndpoint?domain=Parameters
200 OKRecorded 2026-10-25. Send it to run the request live.
{
  "query": {
    "domain": "github.com"
  },
  "state": "enforced",
  "stateRfc7489": "enforced",
  "record": "v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github.com; fo=1",
  "foundAt": "github.com",
  "source": "at_name",
  "inherited": false,
  "organizationalDomain": "github.com",
  "ttl": 263,
  "cname": null,
  "policy": {
    "tag": "p",
    "requested": "quarantine",
    "applied": "quarantine"
  },
  "tags": {
    "p": "quarantine",
    "sp": "reject",
    "np": null,
    "adkim": "r",
    "aspf": "r",
    "testing": false,
    "pct": 100,
    "fo": [
      "1"
    ],
    "psd": "u"
  },
  "outcomes": {
    "domain": {
      "action": "policy",
      "tag": "p",
      "requested": "quarantine",
      "applied": "quarantine",
      "at": "github.com"
    },
    "subdomains": {
      "action": "policy",
      "tag": "sp",
      "requested": "reject",
      "applied": "reject",
      "at": "github.com"
    },
    "nonExistent": {
      "action": "policy",
      "tag": "sp",
      "requested": "reject",
      "applied": "reject",
      "at": "github.com"
    }
  },
  "reports": [
    {
      "type": "aggregate",
      "uri": "mailto:dmarc@github.com",
      "address": "dmarc@github.com",
      "host": "github.com",
      "external": false,
      "authorization": "internal",
      "processor": {
        "id": "github",
        "name": "GitHub"
      },
      "acceptsMail": true
    },
    {
      "type": "failure",
      "uri": "mailto:dmarc@github.com",
      "address": "dmarc@github.com",
      "host": "github.com",
      "external": false,
      "authorization": "internal",
      "processor": {
        "id": "github",
        "name": "GitHub"
      },
      "acceptsMail": true
    }
  ],
  "issues": [
    {
      "severity": "info",
      "code": "pct_removed",
      "message": "pct=100 was removed in RFC 9989: harmless here."
    },
    {
      "severity": "info",
      "code": "ruf_present",
      "message": "1 failure report address (ruf): Gmail and Microsoft never send failure reports, and others may include message headers."
    }
  ],
  "tookMs": 298
}

How it works

Discovers the policy as an RFC 9989 (DMARCbis) receiver does, _dmarc.<domain> and then the DNS tree walk up to the organizational domain, and as a receiver still on RFC 7489 does (the Public Suffix List), side by side: state and stateRfc7489 can differ. The record is parsed tag by tag with defaults filled in, policy says what receivers apply to this domain (after t=y), and outcomes what they do for its subdomains and for names under it that don't exist.

Every rua and ruf address is checked as RFC 9990 §4 has receivers check it: an address outside the domain's organization needs <domain>._report._dmarc.<host> published by the destination, or receivers ignore it; each destination's MX says whether it can take mail at all.

Asks Google Public DNS (Cloudflare's 1.1.1.1 when Google fails) and keeps every question's outcome: a lookup that fails is "couldn't check" (incomplete), never "no record".

Query parameters

  • domainstringrequired
    The domain whose DMARC policy to check (the From domain of the mail). A URL (its host), an email address (its domain), an IDN in Unicode or a trailing dot are read the way the tool page reads them; the normalised name comes back in query.domain.

Response fields

200 with this JSON object, plus tookMs, and incomplete with warnings when a source failed. Fields marked optional appear only when they apply.

  • queryobjectalways
    What was looked up, normalised (lower case, no trailing dot, IDNs as Punycode).
    • domainstringalways
      The domain checked (the RFC5322.From domain), lower case, IDNs as Punycode.
  • statestringalways
    How strong the policy is for RFC 9989 receivers (DMARCbis): enforced (quarantine or reject, applied as written), weak (enforced on paper, but t=y, pct below 100, or sp/np=none at the organizational domain), monitoring (p=none), broken (a record receivers can't use as written), missing (no record at the name or any ancestor), couldnt_check (the lookups that decide it failed: never read as missing).

    one of "enforced", "weak", "monitoring", "broken", "missing", "couldnt_check"

  • stateRfc7489stringalways
    The same reading for receivers still on RFC 7489 (Public Suffix List, pct, no t/np/psd); it can differ.

    one of "enforced", "weak", "monitoring", "broken", "missing", "couldnt_check"

  • recordstring or nullalways
    The record that applies, as published (TXT strings joined); null when none applies.
  • foundAtstring or nullalways
    The name it was found at (_dmarc. + this): the domain itself, or an ancestor.
  • sourcestring or nullalways
    How it was found: at_name (the domain's own), tree_walk (an ancestor's, by RFC 9989's DNS tree walk), psl_org (the Public Suffix List's organizational domain, RFC 7489 only).

    one of "at_name", "tree_walk", "psl_org", null

  • inheritedbooleanalways
    The record is an ancestor's: its sp (or np) decides for this domain.
  • organizationalDomainstring or nullalways
    The organizational domain by RFC 9989's DNS tree walk.
  • ttlinteger or nullalways
    The record's TTL in seconds.
  • cnamearray of strings or nullalways
    When _dmarc.<foundAt> is an alias: each CNAME hop's target, in order (whoever controls the last one controls the policy); null otherwise.
  • policyobject or nullalways
    The policy receivers apply to mail from this domain; null when no usable record applies.
    • tagstringalways
      The tag that decides: p for the domain's own record, sp (or np for a name that doesn't exist) for an inherited one.

      one of "p", "sp", "np"

    • requestedstringalways
      The policy the record asks for.

      one of "none", "quarantine", "reject"

    • appliedstringalways
      What RFC 9989 receivers apply: one level lower under t=y.

      one of "none", "quarantine", "reject"

  • tagsobject or nullalways
    Every tag's effective value, defaults filled in; null when no record applies.
    Show 9 fields
    • pstring or nullalways
      p as published and valid; null when absent or invalid.

      one of "none", "quarantine", "reject", null

    • spstring or nullalways
      sp (subdomains) as published and valid; null when absent (p applies).

      one of "none", "quarantine", "reject", null

    • npstring or nullalways
      np (names that don't exist, RFC 9989) as published and valid; null when absent.

      one of "none", "quarantine", "reject", null

    • adkimstringalways
      DKIM alignment: r relaxed (the default), s strict.

      one of "r", "s"

    • aspfstringalways
      SPF alignment: r relaxed (the default), s strict.

      one of "r", "s"

    • testingbooleanalways
      t=y: RFC 9989's test mode (receivers apply one level lower).
    • pctinteger or nullalways
      pct, when published and valid: removed by RFC 9989; RFC 7489 receivers apply the policy to that share of failing mail.
    • foarray of stringsalways
      fo: when failure reports are asked for (default 0).
    • psdstringalways
      psd: y a public suffix operator's record, n an organizational domain, u unspecified (the default).

      one of "y", "n", "u"

  • outcomesobject or nullalways
    What RFC 9989 receivers do with mail that fails DMARC, for the domain, its existing subdomains and names under it that don't exist; null when the input wasn't a domain.
    • domainobjectalways
      What an RFC 9989 receiver does with failing mail from the domain itself.
      Show 5 fields
      • actionstringalways
        policy (a policy applies), no_dmarc (no usable record: receivers apply no DMARC), unknown (a lookup that decides it failed, or the record repeats a tag).

        one of "policy", "no_dmarc", "unknown"

      • tagstring or nullalways
        The tag that decides; null unless action is policy.

        one of "p", "sp", "np", null

      • requestedstring or nullalways
        The policy the record asks for; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • appliedstring or nullalways
        What receivers apply: one level lower than requested under t=y; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • atstring or nullalways
        The name whose record decides (the DMARC policy domain).
    • subdomainsobjectalways
      What an RFC 9989 receiver does with failing mail from its existing subdomains.
      Show 5 fields
      • actionstringalways
        policy (a policy applies), no_dmarc (no usable record: receivers apply no DMARC), unknown (a lookup that decides it failed, or the record repeats a tag).

        one of "policy", "no_dmarc", "unknown"

      • tagstring or nullalways
        The tag that decides; null unless action is policy.

        one of "p", "sp", "np", null

      • requestedstring or nullalways
        The policy the record asks for; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • appliedstring or nullalways
        What receivers apply: one level lower than requested under t=y; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • atstring or nullalways
        The name whose record decides (the DMARC policy domain).
    • nonExistentobjectalways
      What an RFC 9989 receiver does with failing mail from names under it that don't exist.
      Show 5 fields
      • actionstringalways
        policy (a policy applies), no_dmarc (no usable record: receivers apply no DMARC), unknown (a lookup that decides it failed, or the record repeats a tag).

        one of "policy", "no_dmarc", "unknown"

      • tagstring or nullalways
        The tag that decides; null unless action is policy.

        one of "p", "sp", "np", null

      • requestedstring or nullalways
        The policy the record asks for; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • appliedstring or nullalways
        What receivers apply: one level lower than requested under t=y; null unless action is policy.

        one of "none", "quarantine", "reject", null

      • atstring or nullalways
        The name whose record decides (the DMARC policy domain).
  • reportsarray of objectsalways
    Every usable rua (aggregate) and ruf (failure) address, and whether receivers will send to it (RFC 9990 §4, RFC 9991 §5).
    Show 8 fields
    • typestringalways
      rua (aggregate) or ruf (failure).

      one of "aggregate", "failure"

    • uristringalways
      The URI as written, e.g. mailto:dmarc@example.com.
    • addressstringalways
      The mail address.
    • hoststringalways
      The address's domain.
    • externalboolean or nullalways
      The address is outside the policy's organizational domain, so it needs the destination's authorization; null when that couldn't be decided.
    • authorizationstringalways
      internal (same organization: none needed), authorized (the destination publishes <domain>._report._dmarc.<host>), unauthorized (it doesn't: receivers ignore the address), unknown (the check failed), too_long (the authorization name exceeds DNS limits), override_bad (the destination redirects to another host: no reports), skipped (past the first 10 hosts).

      one of "internal", "authorized", "unauthorized", "unknown", "too_long", "override_bad", "skipped"

    • processorobject or nullalways
      The DMARC report service or company behind the address, when known.
      Show 2 fields
      • idstringalways
        Our stable id for the company, e.g. google, microsoft365, sendgrid.
      • namestringalways
        Its name as it writes it, e.g. Google Workspace.
    • acceptsMailboolean or nullalways
      The address's domain can receive mail (MX, or an address as implicit MX); false for NXDOMAIN, no MX or a null MX; null when unchecked.
  • issuesarray of objectsalways
    The DMARC Checker's findings (RFC 9989, RFC 9990, RFC 9991, RFC 7489), worst first; good news (a reject policy, an authorized destination) is left out. error breaks something (receivers fail it), warning risks it, info is worth knowing.
    • severitystringalways
      error breaks something, warning risks it, info is worth knowing.

      one of "error", "warning", "info"

    • codestringalways
      Stable machine-readable code.
    • messagestringalways
      The problem and what to do about it, in plain English.

Errors

  • 429 rate_limited The domain (its registrable domain) was checked 30 times in the last minute by all callers together, or every caller together reached 600 a minute: retry after Retry-After seconds.

Plus the errors every endpoint can return: 400 for a missing or unreadable parameter, 429 past the limit, 503 when every source failed.

Code samples

curl "https://dnsrobot.net/api/v2/dmarc?domain=github.com"
const params = new URLSearchParams({ domain: "github.com" });
const res = await fetch(`https://dnsrobot.net/api/v2/dmarc?${params}`);
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.policy);
import requests

r = requests.get(
    "https://dnsrobot.net/api/v2/dmarc",
    params={"domain": "github.com"},
    timeout=30,
)
data = r.json()
if not r.ok:
    raise RuntimeError(data["error"]["message"])
print(data["policy"])
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

func main() {
	q := url.Values{"domain": {"github.com"}}
	res, err := http.Get("https://dnsrobot.net/api/v2/dmarc?" + q.Encode())
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	var data map[string]any
	if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
		panic(err)
	}
	if res.StatusCode != http.StatusOK {
		panic(data["error"])
	}
	fmt.Println(data["policy"])
}
<?php
$query = http_build_query(["domain" => "github.com"]);
$context = stream_context_create(["http" => ["ignore_errors" => true, "timeout" => 30]]);
$data = json_decode(file_get_contents("https://dnsrobot.net/api/v2/dmarc?" . $query, false, $context), true);
if (isset($data["error"])) {
    throw new RuntimeException($data["error"]["message"]);
}
print_r($data["policy"]);
require "net/http"
require "json"

uri = URI("https://dnsrobot.net/api/v2/dmarc")
uri.query = URI.encode_www_form(domain: "github.com")
data = JSON.parse(Net::HTTP.get(uri))
raise data["error"]["message"] if data["error"]
puts data["policy"]
// Cargo.toml: reqwest = { version = "0.13", features = ["blocking", "json"] }, serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = reqwest::blocking::get("https://dnsrobot.net/api/v2/dmarc?domain=github.com")?;
    let ok = res.status().is_success();
    let data: serde_json::Value = res.json()?;
    if !ok {
        return Err(data["error"]["message"].to_string().into());
    }
    println!("{}", data["policy"]);
    Ok(())
}
using System.Text.Json;

using var http = new HttpClient();
var res = await http.GetAsync("https://dnsrobot.net/api/v2/dmarc?domain=github.com");
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
if (!res.IsSuccessStatusCode)
    throw new Exception(data.GetProperty("error").GetProperty("message").GetString());
Console.WriteLine(data.GetProperty("policy"));

github.com's policy

GET /api/v2/dmarc?domain=github.com
200 OKRecorded 2026-10-25
{
  "query": {
    "domain": "github.com"
  },
  "state": "enforced",
  "stateRfc7489": "enforced",
  "record": "v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github…",
  "foundAt": "github.com",
  "source": "at_name",
  "inherited": false,
  "organizationalDomain": "github.com",
  "ttl": 263,
  "cname": null,
  "policy": {
    "tag": "p",
    "requested": "quarantine",
    "applied": "quarantine"
  },
  "tags": {
    "p": "quarantine",
    "sp": "reject",
    "np": null,
    "adkim": "r",
    "aspf": "r",
    "testing": false,
    "pct": 100,
    "fo": [
      "1"
    ],
    "psd": "u"
  },
  "outcomes": {
    "domain": {
      "action": "policy",
      "tag": "p",
      "requested": "quarantine",
      "applied": "quarantine",
      "at": "github.com"
    },
    "subdomains": {
      "action": "policy",
      "tag": "sp",
      "requested": "reject",
      "applied": "reject",
      "at": "github.com"
    },
    "nonExistent": {
      "action": "policy",
      "tag": "sp",
      "requested": "reject",
      "applied": "reject",
      "at": "github.com"
    }
  },
  "reports": [
    {
      "type": "aggregate",
      "uri": "mailto:dmarc@github.com",
      "address": "dmarc@github.com",
      "host": "github.com",
      "external": false,
      "authorization": "internal",
      "processor": {
        "id": "github",
        "name": "GitHub"
      },
      "acceptsMail": true
    },
    {
      "type": "failure",
      "uri": "mailto:dmarc@github.com",
      "address": "dmarc@github.com",
      "host": "github.com",
      "external": false,
      "authorization": "internal",
      "processor": {
        "id": "github",
        "name": "GitHub"
      },
      "acceptsMail": true
    }
  ],
  "issues": [
    {
      "severity": "info",
      "code": "pct_removed",
      "message": "pct=100 was removed in RFC 9989: harmless here."
    },
    {
      "severity": "info",
      "code": "ruf_present",
      "message": "1 failure report address (ruf): Gmail and Microsoft never send failure reports, and others may i…"
    }
  ],
  "tookMs": 298
}

Shortened for the page: long lists show their first items.

google.com's policy

GET /api/v2/dmarc?domain=google.com
200 OKRecorded 2026-10-25
{
  "query": {
    "domain": "google.com"
  },
  "state": "enforced",
  "stateRfc7489": "enforced",
  "record": "v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com",
  "foundAt": "google.com",
  "source": "at_name",
  "inherited": false,
  "organizationalDomain": "google.com",
  "ttl": 300,
  "cname": null,
  "policy": {
    "tag": "p",
    "requested": "reject",
    "applied": "reject"
  },
  "tags": {
    "p": "reject",
    "sp": null,
    "np": null,
    "adkim": "r",
    "aspf": "r",
    "testing": false,
    "pct": null,
    "fo": [
      "0"
    ],
    "psd": "u"
  },
  "outcomes": {
    "domain": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "google.com"
    },
    "subdomains": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "google.com"
    },
    "nonExistent": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "google.com"
    }
  },
  "reports": [
    {
      "type": "aggregate",
      "uri": "mailto:mailauth-reports@google.com",
      "address": "mailauth-reports@google.com",
      "host": "google.com",
      "external": false,
      "authorization": "internal",
      "processor": {
        "id": "google",
        "name": "Google"
      },
      "acceptsMail": true
    }
  ],
  "issues": [],
  "tookMs": 39
}

wikipedia.org's policy

GET /api/v2/dmarc?domain=wikipedia.org
200 OKRecorded 2026-10-25
{
  "query": {
    "domain": "wikipedia.org"
  },
  "state": "enforced",
  "stateRfc7489": "enforced",
  "record": "v=DMARC1; p=reject; rua=mailto:dmarc-rua@wikimedia.org;",
  "foundAt": "wikipedia.org",
  "source": "at_name",
  "inherited": false,
  "organizationalDomain": "wikipedia.org",
  "ttl": 600,
  "cname": null,
  "policy": {
    "tag": "p",
    "requested": "reject",
    "applied": "reject"
  },
  "tags": {
    "p": "reject",
    "sp": null,
    "np": null,
    "adkim": "r",
    "aspf": "r",
    "testing": false,
    "pct": null,
    "fo": [
      "0"
    ],
    "psd": "u"
  },
  "outcomes": {
    "domain": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "wikipedia.org"
    },
    "subdomains": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "wikipedia.org"
    },
    "nonExistent": {
      "action": "policy",
      "tag": "p",
      "requested": "reject",
      "applied": "reject",
      "at": "wikipedia.org"
    }
  },
  "reports": [
    {
      "type": "aggregate",
      "uri": "mailto:dmarc-rua@wikimedia.org",
      "address": "dmarc-rua@wikimedia.org",
      "host": "wikimedia.org",
      "external": true,
      "authorization": "authorized",
      "processor": {
        "id": "wikimedia",
        "name": "Wikimedia"
      },
      "acceptsMail": true
    }
  ],
  "issues": [],
  "tookMs": 317
}