propgate docs

API reference › Get startedCheck a domain

Check a domain

POST/v1/checkspropgate check <domain> --remote

Diagnose any domain, against no account and no profile. This is the only endpoint that needs no API key — it is what the checker on propgate.dev calls, and it is the same engine as propgate check and the sweeper.

It reads DNS and writes nothing. Nothing is registered, no state moves, and no webhook fires. If what you want is to re-check a domain you have already registered, that is POST /v1/domains/:id/checks instead.

curl -s -X POST https://api.propgate.dev/v1/checks \
  -H 'content-type: application/json' \
  -d '{"domain":"example.com","checks":["spf"],"spfInclude":"_spf.google.com"}'

Body

FieldTypeDescription
domain*stringThe domain to check. Must have at least two labels and must not be a public suffix.
checksstring[]Which checks to run: delegation, spf, dkim, dmarc, mx, caa, ownership, cname. Defaults to all of them; a check with nothing to compare against is skipped rather than reported.
spfIncludestringAn include: token that must authorise this domain, expanded recursively the way an MTA would.
spfIpstringA specific sending address to evaluate the record against.
dkimSelectorsstring[]Up to ten DKIM selectors to look up.
caaIssuerstringA certificate authority the CAA tree must authorise.
ownershipobject[]Up to five ownership tokens, each { token, label? }. The token is compared byte for byte; omit the label for the apex.
cnamesobject[]Up to five aliases, each { label, target }. The target is resolved, so a provider that flattened the alias into address records passes rather than failing.
expectsMailbooleanTri-state, and deliberately not defaulted. Omit it and the check makes no claim; false asserts the domain receives no mail, which makes a null MX correct rather than a fault.
{
  "data": {
    "object": "check",
    "domain": "example.com",
    "verdict": "fail",
    "elapsedMs": 214,
    "findings": [
      {
        "code": "SPF_SOURCE_NOT_AUTHORIZED",
        "slug": "spf-source-not-authorized",
        "severity": "error",
        "summary": "This domain's SPF record does not authorise the sending service being set up, so its messages will fail SPF.",
        "evidence": {
          "detail": "add include:_spf.google.com before the all mechanism; added after it, the term never runs",
          "observed": "no include: or redirect= terms at all",
          "expected": "include:_spf.google.com"
        }
      }
    ],
    "checks": [
      {
        "kind": "spf",
        "verdict": "fail",
        "findings": ["…"],
        "lookups": [
          {
            "name": "example.com",
            "type": 16,
            "purpose": "the domain's SPF record",
            "server": "10.10.0.10:53",
            "status": "answered"
          }
        ]
      }
    ]
  },
  "error": null,
  "meta": { "resolver": "10.10.0.10:53" }
}

Every finding carries a diagnosis code, what was observed and what was expected. lookups is the derivation: which queries were sent, to which server, and what each returned. A verdict you cannot audit is a verdict you have to take on faith.

Rate limit

20 checks a minute per address, keyed on X-Forwarded-For. A check costs up to about twenty upstream queries aimed at whichever authoritative servers the caller names, so an unauthenticated endpoint is sized against other people's infrastructure rather than ours.

HTTP/1.1 429 Too Many Requests
retry-after: 43

{"data":null,"error":{"message":"too many checks; try again in 43s"},"meta":null}

If you are checking domains on a schedule, register them instead and let the sweeper do it — that is what it is for, and it does not come through here.