mailcheckSPF · DKIM · DMARC

JSON API

Everything the web checker does, as JSON. No key, no signup, no tracking.

Request

GET /api/check?domain=example.com
ParameterRequiredNotes
domainyesBare domain, URL or email address — all are accepted and normalised.
selectorsnoComma-separated DKIM selectors to try in addition to the built-in list. Maximum five.

CORS is open, so you can call this from a browser. OPTIONS is handled.

Response

{
  "version": 1,
  "domain": "example.com",
  "passing": true,
  "counts": { "critical": 0, "warning": 1, "info": 2, "pass": 3 },
  "findings": [
    {
      "id": "spf-lookup-limit",
      "severity": "critical",
      "title": "SPF needs 14 DNS lookups — the limit is 10",
      "detail": "Receivers stop evaluating at ten lookups…",
      "fix": {
        "kind": "record",
        "host": "example.com",
        "type": "TXT",
        "value": "v=spf1 include:… ip4:… -all",
        "caveat": "Flattened records are a snapshot…"
      }
    }
  ],
  "spf":    { "found": true, "lookupCount": 14, "limitExceeded": true, "suggestedRecord": "v=spf1 …" },
  "dkim":   { "selectorsTried": 50, "wildcardDns": false, "keys": [ … ] },
  "dmarc":  { "effectivePolicy": "reject", "externalDestinations": [ … ] },
  "mtaSts": { "mode": "enforce", "unmatchedMx": [] },
  "dnssec": true,
  "meta":   { "dnsQueries": 58, "elapsedMs": 43 }
}

Errors

StatuserrorMeaning
400missing_domainNo domain parameter.
400invalid_domainNot a public domain name — IP addresses and internal names are rejected.
429rate_limitedOver the limit. Retry-After is set.
502check_failedUsually a DNS timeout. Retrying is reasonable.

Limits

10 requests per 60 seconds per IP address. A single check makes around 50 outbound DNS queries, which is why the limit is what it is. If you need more, get in touch rather than working around it — a request that explains what you are building is likely to get a yes.

Stability

The version field is the contract. Fields may be added within a version; anything that removes or changes the meaning of an existing field will increment it. The findings[].id values are stable identifiers — build against those rather than matching on title, which is prose and may be reworded.

Fair use

This runs on one small server and is free because it is cheap to provide at a reasonable volume. Cache results — DNS records change on the order of months, not seconds — and identify your tool in the User-Agent header so a problem can be traced to something other than an anonymous flood.

Example

curl -s "https://notspoofed.com/api/check?domain=paypal.com" | jq '.findings[] | {severity, title}'