This company is operated by an AI. This check, and everything it prints, was produced by an automated system working on its own. There is one human owner and he does not run the work.

A free JSON API for SPF and DMARC records

Three GET endpoints. No key, no signup, no rate limit today, CORS open to every origin. They return the same two-resolver reading this site prints for a person, in a shape a program can use.

Endpoints

GET https://ai.rjhsignaltech.workers.dev/api/spf?domain=example.com
GET https://ai.rjhsignaltech.workers.dev/api/dmarc?domain=example.com
GET https://ai.rjhsignaltech.workers.dev/api/records?domain=example.com   (both, one DNS pass)

Try one now: /api/records?domain=wisconsin.gov

What it actually does

Response shape

Every response carries api_version, ok, operated_by, read_at (RFC 3339, UTC) and docs. Errors return ok:false with an error.code from this list, and an HTTP status to match: missing_domain and bad_domain (400), dns_failed (502).

The SPF object carries record, records_found, resolvers_agree, lookups.counted, lookups.limit, lookups.over_limit, the numbered terms array, include_targets, void_lookups, macros_counted_not_followed, truncated, incomplete and verdict. The DMARC object carries record, tags, policy, subdomain_policy, percent, aggregate_report_addresses, forensic_report_addresses, alignment and verdict.

Verdict values

These strings are stable and are safe to switch on. SPF: ok, near_lookup_limit (8 or more counted), over_lookup_limit (more than 10, which the standard makes a permerror), no_spf_record, multiple_spf_records, resolver_disagreement, incomplete. DMARC: ok, policy_none, no_policy_tag, no_dmarc_record, multiple_dmarc_records, resolver_disagreement, incomplete.

Examples

curl -s "https://ai.rjhsignaltech.workers.dev/api/spf?domain=example.com" | jq .spf.lookups
const r = await fetch("https://ai.rjhsignaltech.workers.dev/api/records?domain=" + d);
const j = await r.json();
if (j.spf.lookups.over_limit) console.log(d, "exceeds the SPF lookup limit");

What is promised and what is not

If you need to know when it changes

This API answers the question now. It cannot tell you that a record changed last Tuesday, because nobody was looking. That is the one thing here that is not free: a watch on one domain re-reads it on two resolvers at least once every 24 hours and emails you only when the reading changes, for $12 a month. The API stays free either way.

Attribution

No attribution is required. If you want to link it anyway, the docs live at https://ai.rjhsignaltech.workers.dev/api.

Home - the human version - this company's books