Email DNS & DMARC Change Monitor avatar

Email DNS & DMARC Change Monitor

Pricing

from $2.50 / 1,000 domain checks

Go to Apify Store
Email DNS & DMARC Change Monitor

Email DNS & DMARC Change Monitor

Monitor SPF, DMARC, MX, and DKIM records, detect configuration changes between runs, and flag email security regressions.

Pricing

from $2.50 / 1,000 domain checks

Rating

0.0

(0)

Developer

Caio Fábio M Silva

Caio Fábio M Silva

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Categories

Share

Monitor SPF, DMARC, MX, and DKIM records and detect configuration changes between runs.

What does Email DNS & DMARC Change Monitor do?

Most DNS tools tell you what your email authentication looks like right now. This Actor tells you what changed — and whether the change made things better or worse.

Give it a list of domains. It queries their public DNS records (MX, SPF, DMARC, and any DKIM selectors you name), normalizes the answers, stores them as a persistent baseline, and on every subsequent run compares the live records against that baseline. Every difference comes out as a typed, classified change:

TransitionChange typeDirection
DMARC p=rejectp=quarantineDMARC_POLICY_WEAKENEDDegraded
DMARC p=quarantinep=noneDMARC_POLICY_WEAKENEDDegraded
DMARC p=nonep=rejectDMARC_POLICY_STRENGTHENEDImproved
SPF record disappearsSPF_REMOVEDDegraded
SPF -all~allSPF_ALL_WEAKENEDDegraded
MX moves from Google Workspace to Microsoft 365MX_CHANGEDNeutral
A configured DKIM selector stops resolvingDKIM_REMOVEDDegraded

Classification is fully deterministic — a fixed set of rules over normalized records. No language model decides whether your domain got safer.

Because it runs on Apify, you can put it on a schedule, call it from the API, and pipe its output into your own alerting.

Why use it?

  • Catch silent regressions. A contractor relaxes p=reject to p=none to unblock a campaign, and nobody tells security. You find out on the next run, not after a phishing incident.
  • Watch your vendors and acquisitions. Track domains you do not control but depend on.
  • Detect email provider migrations. An MX change is often the first public signal of a migration.
  • Notice DKIM key rotation and revocation on the selectors you actually publish.
  • Feed a compliance trail. Every run is a timestamped, structured record of your email DNS posture.
  • No agents, no credentials. It reads only public DNS. Nothing is installed anywhere.

Features

  • MX, SPF, DMARC and explicit-selector DKIM checks
  • Persistent baseline across runs, isolated per monitor and per domain
  • Deterministic change classification with INFO/LOW/MEDIUM/HIGH severity
  • A safe UNKNOWN state that never masquerades as "record removed"
  • Best-effort email provider identification (Google Workspace, Microsoft 365, Proton, Zoho, Mimecast, Proofpoint, Fastmail and more) with no external API calls
  • Bounded concurrency, short retries and per-run DNS health statistics
  • Accepts bare domains, URLs and email addresses; handles IDN and deduplicates automatically

Scan mode

mode: "scan" is a stateless, one-off check. It resolves everything, reports the current configuration, and never reads or writes a baseline. Use it to explore a list of domains, or to audit something once.

Monitor mode

mode: "monitor" requires a monitorId and is where drift detection happens:

  1. First run for a domain — establishes the baseline. No changes are reported, because there is nothing to compare against. baselineCreated is true.
  2. Every later run — loads the baseline, resolves the live records, emits the differences, then rolls the baseline forward.

Each monitorId is a completely separate namespace. production and staging can watch the same domain and never see each other's history.

How to use Email DNS & DMARC Change Monitor

  1. Open the Actor and put your domains in the Domains field.
  2. Add any DKIM selectors you publish (see the DKIM limitation below — they cannot be discovered).
  3. Set Mode to monitor and pick a Monitor ID, e.g. production.
  4. Click Start. The first run establishes the baseline.
  5. Go to the Schedules tab and schedule the same configuration daily (or hourly).
  6. From then on, every run reports what changed. Filter the dataset on changed = true.

Input

FieldTypeDefaultDescription
domainsarrayRequired. Up to 100 domains per run. Accepts example.com, https://example.com/x or user@example.com.
modestringscanscan or monitor.
monitorIdstringRequired for monitor. Letters, digits, - and _, up to 64 chars.
dkimSelectorsarray[]Selectors probed on every domain.
dkimSelectorsByDomainobject{}Extra selectors per domain, e.g. {"example.com": ["selector1"]}.
resolverTimeoutSecondsinteger5Per-query timeout, 1–30.
maxConcurrencyinteger10Domains resolved in parallel, 1–50.
baselineStoreNamestringemail-dns-baselinesNamed key-value store holding baselines.
{
"domains": ["example.com", "example.org"],
"mode": "monitor",
"monitorId": "production",
"dkimSelectors": ["google", "selector1"],
"dkimSelectorsByDomain": { "example.com": ["k1"] },
"maxConcurrency": 10
}

Output

One dataset item per domain. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

{
"domain": "example.com",
"checkedAt": "2026-09-07T14:26:06Z",
"mode": "monitor",
"monitorId": "production",
"changed": true,
"severity": "HIGH",
"changes": [
{
"component": "DMARC",
"type": "DMARC_POLICY_WEAKENED",
"severity": "HIGH",
"direction": "SECURITY_DEGRADED",
"previous": "reject",
"current": "none"
}
],
"mxStatus": "PRESENT",
"mxProvider": "Google Workspace",
"spfStatus": "PRESENT",
"spfAll": "-all",
"dmarcStatus": "PRESENT",
"dmarcPolicy": "none",
"dmarcPct": 100,
"dkimSelectorsChecked": 1,
"dkimPresentCount": 1,
"unknownComponents": 0
}

Each item also carries the full currentStatus and previousStatus structures, plus mx, spf, dmarc and dkim sub-objects for API consumers.

A run-level SUMMARY record is written to the default key-value store with domainsChecked, mxPresent, spfPresent, dmarcPresent, dkimSelectorsChecked, changesDetected, securityImproved, securityDegraded, unknownComponents, executionTimeSeconds, DNS health statistics and billing counters.

Data table

FieldDescription
domainNormalized domain (lowercase, punycode).
checkedAtUTC ISO 8601 observation time.
changedWhether any change was detected against the baseline.
severityWorst severity among this domain's changes.
changes[]Typed changes with severity, direction and before/after values.
mxStatus / mxProviderMX presence and best-effort provider label.
spfStatus / spfAllSPF presence and its all qualifier.
dmarcStatus / dmarcPolicy / dmarcPctDMARC presence, policy and sampling rate.
dkimSelectorsChecked / dkimPresentCountDKIM selectors probed and found.
baselineUpdatedWhether this run wrote a new baseline. Unchanged domains skip the write.
unknownComponentsComponents that could not be resolved this run.
nxdomainThe domain itself does not exist in DNS.

Change types

MXMX_ADDED, MX_REMOVED, MX_CHANGED SPFSPF_ADDED, SPF_REMOVED, SPF_CHANGED, SPF_ALL_WEAKENED, SPF_ALL_STRENGTHENED, SPF_INCLUDES_CHANGED, SPF_REDIRECT_CHANGED DMARCDMARC_ADDED, DMARC_REMOVED, DMARC_CHANGED, DMARC_POLICY_WEAKENED, DMARC_POLICY_STRENGTHENED, DMARC_SUBDOMAIN_POLICY_WEAKENED, DMARC_SUBDOMAIN_POLICY_STRENGTHENED, DMARC_PCT_DECREASED, DMARC_PCT_INCREASED, DMARC_ADKIM_CHANGED, DMARC_ASPF_CHANGED DKIMDKIM_ADDED, DKIM_REMOVED, DKIM_CHANGED

Enforcement strength is ordered none < quarantine < reject for DMARC and +all < ?all < ~all < -all for SPF. Anything moving down that order is SECURITY_DEGRADED; anything moving up is SECURITY_IMPROVED. Changes with no defensible direction (a new MX host, a rotated DKIM key, a reshuffled SPF include list) are NEUTRAL.

Severity

SeverityMeaningExamples
HIGHEnforcement lost, or mail delivery brokenSPF_REMOVED, DMARC_REMOVED, DMARC dropped to none, SPF +all, MX_REMOVED
MEDIUMEnforcement meaningfully relaxed, or infrastructure movedrejectquarantine, -all~all, MX_CHANGED, DKIM_REMOVED
LOWReal but low-impact changeIncludes edited, DKIM key rotated, alignment flags changed
INFONothing changed, or a record was addedSPF_ADDED, DMARC_ADDED
UNKNOWNSomething could not be resolved this runTimeout, SERVFAIL

CRITICAL is deliberately unused. Losing DMARC enforcement is serious, but this Actor sees public DNS only, and severity should not overstate what that evidence supports.

Scheduling

Create a schedule from the Actor's Schedules tab with mode: "monitor" and a stable monitorId. A daily run is enough for most portfolios; hourly is reasonable for a small, high-value set. The baseline lives in a named key-value store, so it persists across schedules, rebuilds and versions.

API

curl -X POST "https://api.apify.com/v2/acts/<ACTOR_ID>/runs?token=<YOUR_TOKEN>" \
-H 'Content-Type: application/json' \
-d '{
"domains": ["example.com"],
"mode": "monitor",
"monitorId": "production"
}'

Fetch results from GET /v2/datasets/<datasetId>/items and the run summary from GET /v2/key-value-stores/<storeId>/records/SUMMARY.

Examples

One-off audit of a domain portfolio

{ "domains": ["example.com", "example.org", "example.net"], "mode": "scan" }

Daily production monitoring with DKIM

{
"domains": ["example.com"],
"mode": "monitor",
"monitorId": "production",
"dkimSelectors": ["google", "selector1", "selector2"]
}

Separate namespaces for separate concerns

Run the same domain under monitorId: "production" and monitorId: "vendor-watch"; the two baselines never interact.

Accuracy and limitations

DNS behavior

DNS is a caching, eventually-consistent system. A record you just published may not be visible to this Actor's resolver until the previous TTL expires, and different resolvers can disagree for a while. A change reported minutes after an edit is expected; treat single observations near a change window with care.

UNKNOWN does not mean absent

This is the most important rule in the product. A timeout, a SERVFAIL, or any resolver error yields UNKNOWN — never MISSING. UNKNOWN components are excluded from comparison and do not overwrite the stored baseline, so a bad network moment cannot manufacture a fake SPF_REMOVED on this run or a fake SPF_ADDED on the next. NXDOMAIN and NOANSWER, which are authoritative answers, do map to MISSING.

DKIM selector limitations

DKIM selectors cannot be discovered from DNS. There is no record that enumerates them; a selector is only findable if you already know its name or read it from a signed message header. This Actor therefore checks only the selectors you provide and makes no attempt to guess. A selector you did not list is simply not checked — it is never reported as missing. Common selectors worth listing: google (Google Workspace), selector1/selector2 (Microsoft 365), k1 (Mailchimp/Mandrill), s1/s2 (SendGrid, various), mail, default, dkim.

Other limits

  • SPF parsing is a normalizer, not a full RFC 7208 evaluator. It does not resolve include: chains, count DNS lookups, or evaluate a specific sending IP.
  • Provider identification is a local hostname heuristic. UNKNOWN is a normal, correct answer for self-hosted or unusual setups.
  • Only the apex domain is checked, not subdomains.
  • Only public DNS is read. There is no SMTP connection, no message sending and no inbox testing.

Troubleshooting

"No changes are reported on my first monitor run." That is correct. The first run establishes the baseline; baselineCreated is true. Changes start on the second run.

"A domain I expected is missing from the output." It was rejected during validation. Check rejectedDomains in the SUMMARY record — IP addresses, single-label names and malformed entries are skipped by design.

"Everything comes back UNKNOWN." Check SUMMARY.dns.unknownRate and errorsByKind. A high timeout count usually means resolverTimeoutSeconds is too low or maxConcurrency is too high for the resolver in use. Try resolverTimeoutSeconds: 10 and maxConcurrency: 5.

"My DKIM selector shows MISSING but I know it exists." Confirm the exact selector name and that <selector>._domainkey.<domain> resolves. Selector names are case-insensitive here but must otherwise match exactly.

"The baseline's updatedAt is older than my last run." That is by design. A monitor run only rewrites a baseline when the configuration actually changed, so updatedAt tells you when the configuration last moved. When the Actor last looked is checkedAt on every dataset item.

"I want to reset a baseline." Use a new monitorId, or delete that monitor's records from the email-dns-baselines key-value store.

Privacy

This Actor reads public DNS records only. It does not collect credentials, mailbox contents, messages, contact lists or login data. It does not send email and does not test SMTP relays.

DMARC rua/ruf reporting destinations are reduced to counts rather than stored as addresses, and DKIM public keys are stored as a short fingerprint rather than the key material. No data leaves the Apify platform: there are no third-party API calls and no proxy is used.

Pricing concept

The intended economic unit is one domain-check: a domain is billed once per run, regardless of how many DNS queries it took (MX + SPF + DMARC + every DKIM selector).

A domain is billed only after its result has been stored in the dataset, never before. Domains rejected during input validation are never billed, a domain is never billed twice in one run, and a domain whose result could not be stored is not billed at all. If a run reaches its charge limit, the Actor stops processing further domains rather than doing work it cannot deliver.

Instrumentation for this is already in the code and inert until a price is configured.

Responsible use

Query only domains you own or have a legitimate interest in monitoring. This Actor reads public records at a modest rate and issues no authenticated requests, but a large domain list on a tight schedule is still traffic pointed at someone else's nameservers. Keep maxConcurrency sensible and your schedule proportionate to how fast the data actually changes — for most portfolios, daily is plenty.

Findings describe public DNS configuration, not the security of an organization. Report them in those terms.