WHOIS Lookup, DNS Records Scraper - DMARC, SPF, DKIM Checker avatar

WHOIS Lookup, DNS Records Scraper - DMARC, SPF, DKIM Checker

Pricing

from $5.90 / 1,000 domain checkeds

Go to Apify Store
WHOIS Lookup, DNS Records Scraper - DMARC, SPF, DKIM Checker

WHOIS Lookup, DNS Records Scraper - DMARC, SPF, DKIM Checker

Bulk domain lookup in one run: WHOIS and RDAP registrar with registration and expiry dates, DNS records including A, AAAA, MX, NS, TXT, CAA, and the SSL certificate issuer with days until expiry. Email security reads SPF, DKIM and DMARC. 1200 zones over RDAP plus 238 more over WHOIS port 43.

Pricing

from $5.90 / 1,000 domain checkeds

Rating

0.0

(0)

Developer

Snow Leo Data

Snow Leo Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

DNS Records API — Bulk Domain Lookup, Email Deliverability

Give it a list of domains. Get back one row per domain containing the DNS zone, the full email-authentication posture, the registration record and the TLS certificate — 80 fields, checked against the standards rather than merely copied out of the resolver.

Run it once and it is a bulk lookup. Run it on a schedule with monitor mode on and it becomes a drift alert: only the domains whose records actually moved come back, each carrying the list of fields that changed and what they used to be.

Everything comes from public sources reached with plain HTTPS, DNS-over-HTTPS and WHOIS on port 43. No API keys, no proxies, no browser, no third-party paid feed.


What it collects

What DNS records does it return?

Seven record types for every domain — A, AAAA, MX, NS, TXT, SOA, CAA — plus the CNAME chain, reverse DNS on request, and the DNSSEC delegation.

The CNAME chain is kept separate from the address records, and that is not a cosmetic decision. Asking a resolver for the A record of a www host usually returns the chain first: measured on 12 September 2026, www.github.com answers with [CNAME, A] and www.airbnb.com with two CNAMEs followed by nine addresses. Any tool that reads the first answer and calls it an address prints github.com in the column marked "IP". Here cname_chain holds the aliases and a_records holds addresses only.

TXT records are reassembled correctly. A TXT string longer than 255 bytes is stored in DNS as several character-strings, and resolvers disagree about how to show them: on google._domainkey.apify.com, Google returns 408 characters with no quotes while Cloudflare returns 413 characters containing four quotes — the same key in two chunks. Stripping the quotes naively inserts a space in the middle of the base64 public key, which produces a DKIM key that looks right, has almost the right length, and is worthless. This Actor concatenates the chunks with nothing between them, the way DNS itself does.

How is the SPF record checked?

Not by printing it. SPF is resolved through its entire include tree and the number of DNS lookups it forces on the receiving server is counted against the limit of 10 set by RFC 7208 §4.6.4. Past that limit the record is a permanent error and receivers ignore it altogether — the domain's mail starts failing SPF with no error message to anybody.

Measured on the Tranco top 300 on 12 September 2026: five domains (1.7 %) are already over the limit, the worst of them needing 24 lookups. Median lookup count is 2, the 90th percentile is 8. Two domains publish more than one SPF record, which RFC 7208 §3.2 also makes a permanent error. None of that is visible in the record text, which is exactly why tools that only show the text report those domains as fine.

The row carries spf_dns_lookups, spf_lookup_limit, spf_lookup_limit_exceeded, the flattened spf_includes list, the all qualifier, and spf_senders — the third-party services allowed to send mail for the domain, recognised from the include tree.

How are DMARC, DKIM and the rest reported?

DMARC is parsed into fields, not left as a string: dmarc_policy, dmarc_subdomain_policy, dmarc_percent, dmarc_rua, dmarc_ruf and both alignment modes. From those comes spoofable, which answers the only question most buyers are actually asking. It is decided by DMARC, not by SPF: a domain with a strict -all SPF and no DMARC record can still be spoofed in the From: header, because SPF checks the envelope. On the Tranco top 300, 79 domains (26.3 %) publish no DMARC at all and another 31 (10.3 %) publish p=none, which only monitors.

DKIM selector names cannot be listed from DNS — they are known only to the sender and the receiver — so they must be guessed. The default list of 15 selectors was chosen by measurement: probing 35 selectors across the Tranco top 120 found at least one key for 53 domains (44 %), and these 15 produced almost all of the hits. The full list of 46 is available in the input; it costs roughly three times the queries for a few percent more coverage.

Wildcard zones are detected before the probing starts. Some zones answer every name under _domainkey with a CNAME. Measured: bing.com returns a CNAME for zzq7x9-no-such-selector._domainkey.bing.com, and a naive prober therefore "finds" all 35 selectors there. And this is not one odd domain — 13 of the Tranco top 150 (8.7 %) behave that way in a live run. This Actor asks for a selector that cannot exist first; if the zone answers, it sets dkim_wildcard: true and returns no selectors rather than dozens of invented ones. The run report counts those zones as dkim_wildcard_zones.

MTA-STS is checked in both halves. The DNS record only announces that a policy exists; the policy applies only if the file at https://mta-sts.<domain>/.well-known/mta-sts.txt actually loads and says mode: enforce. A domain with the record and without the file is protected exactly as much as a domain with neither, and mta_sts_no_policy is a separate finding for precisely that gap.

TLS-RPT, BIMI and DNSSEC round out the set. DNSSEC looks for a DS delegation at the parent as well as the resolver's authenticated-data flag, because the flag alone says the resolver validated something, not that the zone is signed.

Which TLDs does the registration lookup cover?

All of them that publish anything, and getting there takes two different protocols.

RDAP is the modern replacement for WHOIS and returns structured JSON. Counted from IANA's own files on 12 September 2026: the root zone holds 1438 TLDs and 1200 of them have an RDAP server, leaving 238 (16.6 %) with none. That remainder is not obscure — it includes .de, .io, .co, .ch, .it, .eu, .ru, .jp, .cn, .se, .es and .kz. Verified directly: rdap.org/domain/example.de and rdap.org/domain/github.io both answer HTTP 404. A tool built on RDAP alone knows nothing about two of the most common startup TLDs in existence.

Measured on a live cloud run over the Tranco top 150: RDAP answered for 131 domains, WHOIS on port 43 answered for 14 more (9.3 %), and 5 had no registration data anywhere. Those 14 rows are exactly what an RDAP-only tool leaves blank.

So for those zones the Actor falls back to WHOIS on port 43. The responsible server is discovered from whois.iana.org rather than from a hardcoded table — measured: .de resolves to whois.denic.de, .io to whois.nic.io, .co to whois.registry.co — and the answer is parsed into the same fields RDAP produces. registration_source in every row tells you which path answered.

The text parser handles the formats that break naive parsers. .it returns Created: twice — once for the domain and once for the registrant, the latter 24 years later — so the first occurrence wins. .eu writes in blocks: the Registrar: line is empty and the name sits below it as Name:; the parser tracks which block it is inside, and picks up Name: only under the registrar, because under the registrant the same key holds a person's name.

What is never collected?

Registrant, administrative, technical and billing contact data — names, email addresses, telephone numbers, postal addresses. Not when RDAP returns it, not when a WHOIS server prints it. The fields this Actor extracts are the registrar (an organisation), the dates, the registry status codes, the transfer lock and the registry nameservers.

What does the TLS certificate check do?

It opens a real TLS handshake on port 443 with verification enabled and reports issuer, subject, validity window, protocol version, serial and the number of SANs, plus tls_days_until_expiry.

A certificate that fails verification — expired, self-signed, issued for a different name — is reported as the finding tls-invalid, not as a missing certificate. That distinction matters: a silent empty field would tell you the domain has no certificate when in fact it has a broken one, which is the thing you were looking for. A domain that simply does not listen on 443 gets tls_error with the connection failure and no finding, because not serving HTTPS is not a certificate problem.


The posture score

How is the score calculated?

Every domain starts at 100. Each finding subtracts its published weight and the result is clamped to the 0–100 range. The weights are a convention, not a measurement, so the whole table is printed here — a score whose rule is secret tells you nothing.

FindingWeightSeverity
dmarc-missing30critical
spf-missing25high
spf-all-pass25critical
spf-lookup-limit20critical
dmarc-policy-none20high
tls-invalid20critical
spf-multiple-records15high
expiry-past15critical
spf-all-neutral10medium
dkim-none-found10medium
expiry-soon10high
tls-expiry-soon10high
spf-all-softfail5low
spf-include-broken5medium
dmarc-policy-quarantine5low
dmarc-partial-pct5medium
dmarc-no-reports5low
dnssec-unsigned5low
mta-sts-no-policy5medium
transfer-unlocked5medium
nameserver-mismatch5medium
caa-missing3low
mta-sts-missing3low
mta-sts-testing2low
mx-missing0low

Grades: A+ from 95, A from 85, B from 70, C from 55, D from 40, F below that. expiry-soon fires inside 30 days, tls-expiry-soon inside 14.

A finding is only raised for data that was actually collected. Turning off the registration section does not make every domain look transfer-unlocked, and turning off DKIM probing does not accuse every domain of having no key.


Monitor mode

What does monitor mode actually remember?

Twenty fields per domain, stored in a named key-value store so the memory survives between runs: the CNAME chain, MX hosts, nameservers, TXT, CAA, the SPF record and its over-limit flag, the DMARC record and policy, the DKIM selectors found, MTA-STS mode, DNSSEC state, registrar, domain expiry date, registry status, registry nameservers, mail provider, DNS provider, the certificate expiry date and the posture score.

Each returned row carries change_type (NEW, UPDATED, UNCHANGED), changed_fields listing exactly which of the twenty moved, and previous_values holding the earlier value for the five short fields worth keeping in full — DMARC policy, posture score, expiry date, registrar and mail provider.

Unchanged domains are not written to the dataset, so a daily run over a stable list costs you nothing in result rows. Set emitUnchanged if you want them anyway.

Why are A and AAAA records not watched by default?

Because they move on their own. Two cloud runs nine seconds apart reported four of five domains as changed, and in every case the only difference was the address set. Checked again with four snapshots ten seconds apart across the ten prefill domains: three of them returned a different address set — apify.com on AAAA, stripe.com and vercel.com on A — while MX, NS, TXT and CAA did not move for a single domain out of the Tranco top 100.

That is load balancing, not a zone edit. Watching it would make anyone monitoring CDN-backed domains pay for their whole list every single run. If a change of address is what you are after, watchAddressRecords adds a_records and aaaa_records to the watch list.

Why do list order changes not count as changes?

Because resolvers shuffle. The order of A records and TXT strings differs between two consecutive queries — that is load balancing, not a zone edit. Lists are normalised by sorting before fingerprinting; without that, every domain would look updated on every run and you would pay for the entire list daily.

The same care applies to new fields: a field that was not in the previous run's memory is not reported as changed, otherwise one update to this Actor would mark every domain in your list as updated and bill you for all of them at once.

Memory holds 20 000 domains. Beyond that the oldest entries are dropped first.

What can monitor mode tell me that a lookup cannot?

An MX change means a mail migration just happened — the best possible moment for a vendor conversation. A weakened DMARC policy means someone turned enforcement off, usually while debugging a campaign, and often forgot to turn it back on. A changed nameserver set with no corresponding registry change is what a hijack looks like. A registrar change on a portfolio you do not own is a competitor consolidating. An approaching expiry date is a renewal you are about to miss.


Filters, and the reason they are careful

Do filters cost me money?

No. Every filter runs after the lookups and before the dataset write, so a domain removed by a filter is never charged.

Can a filter return nothing by accident?

Two protections make that unlikely. First, choosing a finding code that can only come from a section you switched off turns that section back on for the run and says so in the log — asking for transfer-unlocked with registration disabled would otherwise return zero rows from a perfectly green run.

Second, a filter never drops a domain merely because a value is unknown. expiringWithinDays keeps domains whose expiry date could not be determined — some zones publish none and some WHOIS servers rate-limit — and counts them separately in the run report as _kept_expiry_unknown. Dropping them would quietly assert that they are safe.

When a run does legitimately produce nothing, it says why: the report carries empty_reason (nothing-changed, filtered-out, source-returned-nothing, charge-limit-reached) and the log carries a warning naming the number.


Output shape

What do I get per row?

80 fields in the default shape, arranged into five dataset views: Domains (the decision columns), Email security, DNS records, Registration and TLS certificates.

Monitor mode adds three more: change_type, changed_fields and previous_values.

compactOutput reduces the row to 26 decision fields for AI agents and spreadsheets. excludeEmptyFields drops nulls and empty lists. Neither changes which domains are checked or charged — they are shape, not filter.

An empty cell always means the domain does not publish that record. Nothing is inferred, estimated or filled in from a model.

How fast is it and how is the load handled?

Eight domains are processed in parallel; each domain costs roughly 40 DNS queries plus optional WHOIS and TLS connections. Measured on the platform: the built-in ten-domain sample finishes in 4.9 seconds, and 150 domains from the Tranco list in 284 seconds — about 1.9 seconds per domain once WHOIS rate-limits and unreachable HTTPS ports are in the mix. Results are written and the monitor memory is saved every 40 domains, so an interrupted run loses at most one batch — and the order is always deliver first, record second, never the reverse.

Rate limiting is shared across threads by host: when a host asks for a pause, every thread waits, rather than the sleeping one waiting while the rest keep hammering.


Honest limits

  • WHOIS servers rate-limit. Measured: whois.domain-registry.nl stopped answering after a handful of queries in one session. Large batches in the non-RDAP zones will produce some rows with registration_error filled in instead of dates. RDAP zones — 1200 of the 1438 — do not have this problem.
  • DKIM coverage is a probe, not an enumeration. 44 % of the Tranco top 120 yielded a key to 35 selectors. An empty dkim_selectors_found means "not among the selectors tried", not "no DKIM".
  • Many domains do not serve HTTPS on the apex. In the Tranco top 150 run, 36 domains (24 %) could not be connected to on port 443 at all. Those get tls_error with the connection failure and no finding — not serving HTTPS is not a certificate problem. Three domains in the same run had a certificate that failed verification, and those are findings.
  • No HTTP status or page content. This Actor inspects DNS, registry and the TLS certificate. It does not fetch the website.
  • No Standby / instant-API mode. Standby would exclude this Actor from Apify's agentic-payments listing, which is worth more than the convenience.
  • IPv6 reverse DNS is not looked up. The nibble form produces a 72-character query name that is almost never populated, and it would cost a query per address.
  • Internationalised domains must be supplied in punycode. Raw Unicode names are rejected with a warning rather than silently mangled.
  • Certificate Transparency logs are not queried. crt.sh answered HTTP 502 to a wildcard query during testing and took 14.7 seconds for a single domain; it is not dependable enough for a run that must finish.

FAQ

Do I need an API key for anything?

No. DNS-over-HTTPS at Google and Cloudflare, the IANA RDAP bootstrap, the RDAP servers themselves, whois.iana.org, the registry WHOIS servers and the MTA-STS policy files are all public and unauthenticated. Nothing here needs a proxy or a headless browser either.

What happens if a domain does not exist?

The row still comes back, with resolved: false and dns_status: NXDOMAIN. Use onlyResolved to drop those. A domain that answers NOERROR with no A record — common for mail-only domains — counts as resolved if it has MX, NS or TXT.

Can I feed it URLs or email addresses instead of domains?

Yes. https://www.example.co.uk/pricing?ref=1 and sales@example.co.uk are both accepted; schemes, paths, queries, ports, trailing dots and letter case are normalised away. Duplicates are removed before anything is charged, and anything that cannot be read as a domain is reported in the log and counted in the report rather than silently dropped.

How do I use it as a daily email-security monitor?

Put your domain list in, switch on Monitor mode, and schedule the Actor daily. The first run returns everything as NEW and fills the memory. Later runs return only the domains that moved. Combine with minSeverity: high to be told only about the serious drift.

How do I find spoofable domains in a prospect list?

Switch on onlySpoofable, or filter on findingCodes for dmarc-missing and dmarc-policy-none. Every returned row already carries the posture score, the worst severity and the finding list, so the output sorts itself.

How do I watch a domain portfolio for expiry and hijacking?

Use expiringWithinDays for renewals, and monitor mode for the rest: a change in registry_status, registry_nameservers or registrar is what an unauthorised transfer looks like. transfer_locked tells you whether the registry lock is even in place.

Why does one of my domains show a lower score than I expect?

Open findings on that row. Every entry names the code, the severity, the weight it subtracted and a sentence explaining it. The score is only the sum; the findings are the substance.

Does the daily run cost the same as the first one?

In monitor mode, no. Only changed domains are written, and only written rows are billable results. A stable list of a thousand domains that nobody touched returns zero rows and says empty_reason: nothing-changed.

What is in the run report?

REPORT in the key-value store: domains requested and checked, rows written, unusable input entries, which source answered for registration, how many zones answer DKIM probes with a wildcard, how many SPF records are over the RFC limit, how many TLS certificates failed verification versus were unreachable, the change counts, and exactly what each filter removed before you were charged.

What else is this called?

Names buyers use: WHOIS bulk lookup, DNS records scraper, DMARC checker, DMARC policy audit, SPF record checker, SPF lookup limit, DKIM selector discovery, MX record lookup, WHOIS expiry alert, domain expiry monitor, RDAP lookup API. All of them read the same record set this Actor returns.

Other names for what this returns: reverse DNS PTR lookup, DMARC RUA aggregate reports, SMTP TLS reporting, DNSSEC validation check, SPF too many DNS lookups, domain expiration date checker, SSL certificate expiry alert and SSL expiry monitor, SaaS vendor detection over DNS, email security posture score, registrar IANA id, MX record health check. Each of those is a field in the row: reverse_dns, dmarc_rua, tls_rpt_record, dnssec_signed, spf_dns_lookups, expires_at, tls_days_until_expiry, verified_services, posture_grade, registrar_iana_id, mx_records.