Bulk DNS Lookup: MX, SPF & DMARC Checker, Email Provider
Pricing
from $2.00 / 1,000 domain checkeds
Bulk DNS Lookup: MX, SPF & DMARC Checker, Email Provider
Bulk DNS lookup: MX, SPF and DMARC checker, email provider (Google Workspace, Microsoft 365), A, TXT, NS, CAA. No WHOIS. $2 per 1,000 domains.
Pricing
from $2.00 / 1,000 domain checkeds
Rating
0.0
(0)
Developer
Kaizoku Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 hours ago
Last modified
Categories
Share
Is the prospect on Google Workspace or Microsoft 365? Are their SPF and DMARC records set up? Does the domain even exist? Paste a list of domains (or URLs, or email addresses) and get one clean row per domain: A, AAAA, MX, TXT, NS, CNAME and CAA records, the email provider, and an SPF and DMARC check.
Price: $2.00 per 1,000 domains ($0.002 per domain), everything included. Invalid entries, duplicates and domains the resolvers couldn't answer are free.
- MX record checker and email provider detection: Google Workspace, Microsoft 365, Zoho, Proton and 20+ other providers, with a best-effort guess when the MX shows only a filtering gateway (Proofpoint, Mimecast, Barracuda).
- SPF checker:
-allor~all, lookup-count estimate, several SPF records, unknown terms. - DMARC checker: policy (
none,quarantine,reject), subdomain policy,pct, and the domains that receive the reports. - Dead-domain cleaning: NXDOMAIN rows come back as
exists: false, so you can drop them before outreach. - Quick start: press Start. The prefilled input checks
example.comandgithub.comin a few seconds for $0.004. No API keys, no proxies, no browser: the Actor asks public DNS-over-HTTPS resolvers.
DNS only, no WHOIS. It does not return registrar, owner, creation or expiry dates. If you need those, use a WHOIS/RDAP Actor.
What you can do with it
- Lead qualification. Is the prospect on Google Workspace or Microsoft 365? Does the domain even exist? Filter a list before outreach.
- Email deliverability audits. Check SPF (
-allor~all, lookup count, several records) and DMARC (none,quarantine,reject) across hundreds of client or vendor domains. - Security and brand monitoring. Spot dangling CNAMEs, changed name servers, missing CAA, or lookalike domains that resolve.
- Migration checks. Confirm that A, MX and NS records point where you expect after a DNS move.
- Dead-domain cleaning. NXDOMAIN rows are reported as
exists: false, so you can drop unregistered domains from a list.
Input
| Field | What it does |
|---|---|
| Domains | A list. Bare domains, URLs (https://example.com/page) and email addresses (the domain part is used) all work. Names are lower-cased, converted to punycode (bücher.ch becomes xn--bcher-kva.ch) and de-duplicated. Entries that are not valid domain names are skipped and not charged. |
| Record types | Any of A, AAAA, MX, TXT, NS, CNAME, CAA. Default: all except CAA. |
| Detect email provider | On by default. Works out the mail provider from the MX records. |
| Check SPF and DMARC | On by default. Parses the SPF record and the _dmarc policy. |
| Strip "www." | Off by default. Turns www.example.com into example.com. |
| Max domains | Look up at most this many domains. |
Names are looked up exactly as given: a subdomain such as mail.example.com is queried as itself (it will usually have no NS records of its own).
Example inputs
1. Lead qualification: who hosts the prospect's email? Paste email addresses or website URLs straight from your CRM export; only the domain part is used.
{"domains": ["sales@acme.io", "https://www.stripe.com/pricing", "github.com"],"recordTypes": ["MX"],"detectEmailProvider": true,"checkSpfDmarc": true,"stripWww": true}
2. Email deliverability audit of client or vendor domains (SPF and DMARC are checked whatever record types you pick; TXT shows the raw records):
{"domains": ["example.com", "github.com", "bbc.co.uk"],"recordTypes": ["MX", "TXT"],"detectEmailProvider": true,"checkSpfDmarc": true}
3. DNS migration or security check (addresses, name servers, aliases and CAA only; email checks off):
{"domains": ["example.com", "www.example.com", "github.com"],"recordTypes": ["A", "AAAA", "NS", "CNAME", "CAA"],"detectEmailProvider": false,"checkSpfDmarc": false,"maxDomains": 5000}
Output
One item per domain (trimmed example; real runs include all fields below):
{"domain": "github.com","exists": true,"records": {"A": ["20.205.243.166"],"AAAA": [],"MX": [{ "priority": 0, "exchange": "github-com.mail.protection.outlook.com" }],"TXT": ["v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com … ~all"],"NS": ["dns1.p08.nsone.net", "ns-1283.awsdns-32.org"],"CNAME": [],"CAA": []},"ttl": { "A": 60, "AAAA": null, "MX": 300, "TXT": 300, "NS": 900, "CNAME": null, "CAA": null },"emailProvider": "Microsoft 365","emailProviders": ["Microsoft 365"],"spf": {"present": true,"record": "v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com … ~all","includes": ["spf.protection.outlook.com", "mail.zendesk.com", "sendgrid.net"],"mechanisms": ["ip4:192.30.252.0/22", "include:spf.protection.outlook.com", "~all"],"redirect": null,"all": "~all","lookupCountEstimate": 8,"valid": true,"issues": []},"dmarc": {"present": true,"record": "v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:[email removed]; ruf=mailto:[email removed]; fo=1","policy": "quarantine","subdomainPolicy": "reject","pct": 100,"ruaDomains": ["github.com"],"rufDomains": ["github.com"],"valid": true},"recordTypes": ["A", "AAAA", "MX", "TXT", "NS", "CNAME", "CAA"],"resolver": "cloudflare","checkedAt": "2026-10-18T06:00:00.000Z","source": "Public DNS over HTTPS (Cloudflare 1.1.1.1, AdGuard DNS as fallback)","error": null}
A domain that does not exist comes back as "exists": false with empty record lists. Export as JSON, CSV, Excel or XML, or read it through the Apify API. The dataset has three views: Domains, Email security and All records.
Notes on the fields:
recordsalways has all seven keys. A type you did not request is an empty array;recordTypestells you what was requested.ttlis the lowest TTL per type as reported by the resolver. A cached answer shows the time left, so it can be lower than the zone's configured TTL.emailProvideris the provider of the lowest-priority recognised MX host. It isotherwhen MX records exist but none is recognised, andnonewhen there is no MX or a null MX. Recognised: Google Workspace, Microsoft 365 (including*.mx.microsoftandprotection.outlook.ushosts), Zoho Mail, Proton Mail, Fastmail, iCloud Mail, Yahoo Mail, Amazon SES / WorkMail, Mimecast, Proofpoint, Barracuda, Cloudflare Email Routing, GoDaddy, OVH, IONOS, Yandex, Mail.ru, Tencent / QQ, Namecheap Private Email and Email Forwarding, Hostinger, Gandi, Rackspace, Cisco Secure Email, Mailgun, SendGrid, Zimbra.emailProviderslists every recognised provider from the MX set. Gateways: when the MX shows only a filtering gateway (Proofpoint, Mimecast, Barracuda and similar), the real mailbox provider is not visible in MX. In that case, if Check SPF and DMARC is on, we add a best-effort guess from the SPF includes (for examplespf.protection.outlook.comsuggests Microsoft 365,_spf.google.comsuggests Google Workspace) toemailProviders. It is an inference, not proof:emailProviderkeeps naming what the MX says.spf.lookupCountEstimatecounts the lookup terms in the domain's own record (include,a,mx,ptr,exists,redirect). It does not follow includes, so the true total can be higher.spf.validis false when there is no record, there are several SPF records, a term is unknown, or more than 10 lookups are counted.existsisfalseonly when the resolver answers NXDOMAIN and nothing else shows the name alive. A dangling alias (a CNAME whose target is gone) counts as existing. If the answers contradict each other (NXDOMAIN for one type, data for another),existsisnullwith anerrorand the row is not charged.- Records belong to the name you asked for. An alias has no NS, MX or TXT of its own, so a CNAME'd subdomain shows empty NS/MX/TXT rather than its target's records.
- CNAME chains: like any resolver, an
Alookup of a name that is an alias returns the addresses at the end of the chain.
Fill rates in our recorded sample of 99 existing domains (2026-10-17): NS 100%, A 98%, TXT 97%, MX 95%, SPF present 96%, DMARC present 95%, AAAA 43%, CAA 48%, CNAME 1%. Low numbers are records most domains simply don't have.
Pricing
Pay per event:
- $0.002 per domain checked.
- Apify's standard tiny start fee per run.
| Domains | Cost |
|---|---|
| 100 | $0.20 |
| 1,000 | $2.00 |
| 10,000 | $20.00 |
You are charged once per input domain that got a definitive answer, including "this domain does not exist" (NXDOMAIN). You are not charged for entries that are not valid domains, for duplicates, or for domains the resolvers could not answer (those rows carry an error message so you can retry them). You can cap any run with Apify's Max total charge setting; the Actor stops cleanly when it reaches it.
Use it via API, n8n or Make
Get an API token in Apify Console → Settings → API & Integrations, and keep it in an environment variable (APIFY_TOKEN below). Never paste it into shared code.
cURL (starts a run, waits for it and returns the rows as JSON; this synchronous endpoint waits up to 5 minutes, which fits a few hundred domains; for bigger lists use the clients below):
curl -X POST "https://api.apify.com/v2/acts/ataism~dns-records/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"domains": ["github.com", "stripe.com"], "recordTypes": ["MX", "TXT"]}'
JavaScript (npm install apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('ataism/dns-records').call({domains: ['github.com', 'stripe.com'],recordTypes: ['MX', 'TXT'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const row of items) console.log(row.domain, row.emailProvider, row.dmarc?.policy);
Python (pip install "apify-client>=3"):
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("ataism/dns-records").call(run_input={"domains": ["github.com", "stripe.com"],"recordTypes": ["MX", "TXT"],})for row in client.dataset(run.default_dataset_id).iterate_items():print(row["domain"], row["emailProvider"], (row.get("dmarc") or {}).get("policy"))
(With apify-client versions before 3.0, call() returns a dict: use run["defaultDatasetId"].)
n8n (enrich a sheet of domains):
- Read the domain column with a Google Sheets node and combine it into one list (for example with an Aggregate node).
- Add the Apify node, operation Run an Actor and get dataset, Actor
ataism/dns-records, input{"domains": <your list>}. - Write
emailProvider,spf.valid,dmarc.policyandexistsback with a Google Sheets update node, matching ondomain.
Make:
- Add the Apify module Run an Actor, choose
ataism/dns-records, map your domain list into the input JSON and set Run synchronously to Yes. - Add the Apify module Get Dataset Items and map the run's default dataset ID.
- Add Google Sheets (Bulk Add Rows or Update a Row) or your CRM and map the fields.
Privacy
DNS records are public information about organisations. This Actor:
- does no WHOIS or RDAP lookups, so it never returns registrant names, emails or phone numbers;
- does not output the SOA record, whose contact field is a person's or team's mailbox;
- replaces any email address found inside TXT or CAA values with
[email removed]; - reduces DMARC
rua/rufreport addresses to their receiving domains only (ruaDomains,rufDomains); the rawmailto:addresses are never output.
The resolvers you query through (Cloudflare, with AdGuard as backup) see the domain names you look up, as with any DNS query.
Reliability
- Uses two public DNS-over-HTTPS JSON resolvers: Cloudflare 1.1.1.1 first, AdGuard DNS (non-filtering) as fallback. An NXDOMAIN answer is final and is not second-guessed.
- At most 5 queries in flight, a timeout per query, and retries with exponential backoff on timeouts, HTTP 429 and 5xx (honouring
Retry-After). It never tries to bypass a limit. - Domains that fail on both resolvers are returned with an
errorand no charge. - A daily self-test queries 10 stable domains (for example example.com, github.com, bbc.co.uk) plus a made-up name, checks fixed expectations (such as "example.com has A and NS records") rather than exact IPs, cross-checks NS records between the two resolvers, and compares field fill rates and the resolver response format with a recorded baseline. Breakages are caught within a day.
FAQ
Is it allowed? Yes. DNS records are public by design, and we query public resolvers through their documented DNS-over-HTTPS JSON interfaces, at a low and polite rate. What the providers publish (checked 2026-10-18):
- Cloudflare 1.1.1.1 (primary). Its DNS JSON API page (developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json) documents the endpoint for general use and states no rate limit or acceptable-use clause; it only warns that the JSON format has no RFC, so "behavior might be different between providers". Its privacy page (developers.cloudflare.com/1.1.1.1/privacy/public-dns-resolver) says the resolver "is governed by our Privacy Policy", that "all Public Resolver Logs are deleted within 25 hours", and that Cloudflare "does not block or filter any content through the 1.1.1.1 Public DNS Resolver". No rate limits are mentioned there either.
- AdGuard DNS (fallback, only used when Cloudflare fails). Its public DNS page (adguard-dns.io/en/public-dns.html) lists the non-filtering server and says nothing about the JSON
/resolveAPI, rate limits or acceptable use; its privacy page (adguard-dns.io/en/privacy.html) says "We do not process any of your Personal Data when you connect to and use public AdGuard DNS." Because its terms are silent on automated use, it is a low-volume backup and the self-test's 10 cross-check queries only. - Not used: Google Public DNS (excluded by project policy) and Quad9: its JSON endpoint on port 5053 did not answer from our test machine (connection timeout) and its current docs list only the standard RFC 8484 endpoint (
https://dns.quad9.net/dns-query), which does not return JSON.
We keep to at most 5 queries in flight, back off on errors and never try to evade a limit. If a provider publishes limits or objects, we will change the Actor.
Do you provide WHOIS data (registrar, owner, expiry)? No, on purpose. See Privacy.
Why is exists false for a domain I know? The resolver reported NXDOMAIN for that exact name. Check for typos, and note that subdomains are looked up as given.
Why does a subdomain have no NS records? NS records are normally published at the zone apex (example.com), not on every subdomain.
Does it do reverse DNS, DNSSEC validation or DKIM? Not in this version. DKIM needs the selector, which is not discoverable from DNS.
Something's wrong? Open an issue on the Actor's Issues tab. We aim to reply within 2 working days. Not affiliated with Cloudflare, AdGuard, or any email provider named above.