# DNS Records Lookup: A, AAAA, MX, NS, TXT, SPF, DMARC, CAA (`deriverge/dns-records-lookup`) Actor

Look up DNS records in bulk: A, AAAA, CNAME, MX, NS, TXT, SOA and CAA for any hostname via DNS over HTTPS, plus the SPF record, DMARC policy and detected mail provider. Changes-only mode for DNS monitoring. JSON, CSV and API.

- **URL**: https://apify.com/deriverge/dns-records-lookup.md
- **Developed by:** [deriverge s.r.o.](https://apify.com/deriverge) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 dns lookups

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## DNS Records Lookup

DNS Records Lookup reads the DNS records of a list of hostnames and returns one row per hostname: A and AAAA addresses, the CNAME target, MX mail servers by priority, nameservers, TXT, SOA and CAA records, plus the SPF record, the DMARC policy and the mail provider. Paste hostnames, URLs or email addresses into **Hostnames** and click **Start**; 104 company domains took 4 seconds in October 2026. DKIM keys are not looked up, because their DNS name depends on a selector that only the sender knows. It costs $0.80 per 1,000 DNS lookups on the Free plan ($0.40 on Business), and the $5 of monthly credit in Apify's Free plan covers about 6,200 DNS lookups.

### What data does it return?

| Field | Description |
|---|---|
| `input`, `hostname` | The value you entered and the hostname taken from it, in lower case, with internationalized names converted to punycode. |
| `domain`, `publicSuffix`, `tld` | The registrable domain found with the Public Suffix List (`apify.com` for `blog.apify.com`), its public suffix such as `co.uk`, and the last label such as `uk`. |
| `a`, `aaaa` | IPv4 and IPv6 addresses of the hostname, or of the final target when it is an alias. |
| `cname` | Target of the CNAME record when the hostname is an alias, such as `apify.ghost.io` for `blog.apify.com`. `null` otherwise, and always for a bare domain. |
| `mx` | Mail servers, each with `priority` and `exchange`, the preferred one (lowest number) first. |
| `ns` | Nameservers in lower case. Empty for a subdomain that is not delegated to its own nameservers. |
| `txt` | Every TXT record with quotes removed and split strings joined: SPF, site verification tokens and similar entries. |
| `soa` | Start of authority: `primaryNs`, `admin` (the hostmaster mailbox written as a hostname), `serial`, and `refresh`, `retry`, `expire` and `minimum` in seconds. `null` below the top of a zone. |
| `caa` | CAA records naming the certificate authorities allowed to issue certificates, such as `0 issue "letsencrypt.org"`. |
| `spf` | The TXT record that starts with `v=spf1`, `null` when there is none. |
| `dmarc`, `dmarcPolicy` | The record published at `_dmarc.<hostname>` and its policy: `none`, `quarantine` or `reject`. |
| `mailProvider` | Mail service recognized from the MX hostnames among 22 known ones, such as `Google Workspace`, `Microsoft 365`, `Proofpoint` or `Seznam.cz`. `Other` when none matches, `null` without MX records. |
| `websiteResolves`, `hasMx` | `true` when the hostname has an A, AAAA or CNAME record, and when it has at least one MX record. `null` when no resolver answered. |
| `notes` | Problems met for this hostname, such as `DNS: name does not exist (NXDOMAIN)`. Empty when everything went fine. |
| `type`, `key`, `checkedAt` | Always `domain`, the key `domain:<hostname>` that changes-only mode compares, and the time of the lookup in UTC. |
| `registered`, `registrar`, `createdAt`, `expiresAt`, `nameservers`, `subdomains` | These and the other registration fields are always `null` or empty here. They keep the row in the same shape as our WHOIS actors, which fill them. |

### How to look up DNS records in bulk

1. Enter names in **Hostnames**, one per line: `example.com`, `mail.example.com`, a URL such as `https://www.example.com/pricing` or an email address such as `info@example.com`. The hostname is taken from each value and looked up exactly, so `www.example.com` and `example.com` are separate rows.
2. In **DNS record types**, keep all eight types or pick the ones you need, such as `MX` and `TXT` for an email audit.
3. Leave **Email setup checks** on to get `spf`, `dmarc`, `dmarcPolicy` and `mailProvider`. It adds the DMARC query and, when you did not pick them, the MX and TXT queries, at no extra charge.
4. Click **Start**. The **Output** tab shows one row per hostname, which you can download as CSV, Excel or JSON. Values that are not domain names, such as IP addresses, are skipped and named in the log.

An API input that checks the mail setup of three domains every day and returns only those whose records moved:

```json
{
  "domains": ["example.com", "info@example.org", "https://shop.example.net/"],
  "dnsTypes": ["MX", "TXT", "NS"],
  "emailChecks": true,
  "changesOnly": true,
  "watchName": "our-mail-domains"
}
```

### Example output

The row for `apify.com` from a run on 11 October 2026 with all record types and email checks on.

```json
{
  "type": "domain",
  "key": "domain:apify.com",
  "input": "apify.com",
  "hostname": "apify.com",
  "domain": "apify.com",
  "tld": "com",
  "publicSuffix": "com",
  "registered": null,
  "lookupSource": null,
  "rdapServer": null,
  "whoisServer": null,
  "registrar": null,
  "registrarIanaId": null,
  "registrarUrl": null,
  "createdAt": null,
  "updatedAt": null,
  "expiresAt": null,
  "transferredAt": null,
  "domainAgeDays": null,
  "daysUntilExpiry": null,
  "statuses": [],
  "nameservers": [],
  "dnssec": null,
  "a": [
    "3.170.42.100",
    "3.170.42.109",
    "3.170.42.68",
    "3.170.42.76"
  ],
  "aaaa": [
    "2600:9000:2870:1200:9:a03e:6540:93a1",
    "2600:9000:2870:3000:9:a03e:6540:93a1",
    "2600:9000:2870:3c00:9:a03e:6540:93a1",
    "2600:9000:2870:9600:9:a03e:6540:93a1",
    "2600:9000:2870:c00:9:a03e:6540:93a1",
    "2600:9000:2870:d400:9:a03e:6540:93a1",
    "2600:9000:2870:da00:9:a03e:6540:93a1",
    "2600:9000:2870:e00:9:a03e:6540:93a1"
  ],
  "cname": null,
  "mx": [
    {
      "priority": 1,
      "exchange": "aspmx.l.google.com"
    },
    {
      "priority": 5,
      "exchange": "alt1.aspmx.l.google.com"
    },
    {
      "priority": 5,
      "exchange": "alt2.aspmx.l.google.com"
    },
    {
      "priority": 10,
      "exchange": "aspmx2.googlemail.com"
    },
    {
      "priority": 10,
      "exchange": "aspmx3.googlemail.com"
    }
  ],
  "ns": [
    "ns-1225.awsdns-25.org",
    "ns-1928.awsdns-49.co.uk",
    "ns-449.awsdns-56.com",
    "ns-839.awsdns-40.net"
  ],
  "txt": [
    "aisle-domain-verification-ymx7gd=j3J4hCmXHIc9R0RNQCfa8xdSf",
    "google-site-verification=R_4agUZyHorQrO5QGFa6ZB_o-uDxq82NY-EteDUpPvo",
    "google-site-verification=XU737OZxx6NvGd3NQK9MRt98qxRMjx3RByq-O0cy16I",
    "google-site-verification=ZqRh2_YSBHj2bo_KsEdu6MfmTMKJYLpDDL3oPtOrN_A",
    "google-site-verification=jrECJwBG9eeO_FUrOMhp7JY1oF8JSBxhzmLsLGvrt9s",
    "mixpanel-domain-verify=44615d9c-27f2-4587-a781-8a0a1c734e9a",
    "openai-domain-verification=dv-WTM7Nna92Owwh4sgmDOVUyyt",
    "tito-domain-verification=pbg2696rvzbax2t4dl7h5ynqm",
    "v=MCPv1; k=ed25519; p=1rDnoEigwTr+4JjG89+sLbdNnswCnLjmaeT34srW53Y=",
    "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com include:19497222.spf05.hubspotemail.net -all",
    "zapier-domain-verification-challenge=2c358aa9-fa36-419f-b21a-ca9eb6f2791d"
  ],
  "soa": {
    "primaryNs": "ns-839.awsdns-40.net",
    "admin": "awsdns-hostmaster.amazon.com",
    "serial": 1,
    "refresh": 7200,
    "retry": 900,
    "expire": 1209600,
    "minimum": 86400
  },
  "caa": [
    "0 issue \"amazonaws.com\"",
    "0 issue \"digicert.com\"",
    "0 issue \"letsencrypt.org\"",
    "0 issuewild \"amazonaws.com\"",
    "0 issuewild \"letsencrypt.org\""
  ],
  "websiteResolves": true,
  "hasMx": true,
  "mailProvider": "Google Workspace",
  "spf": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com include:19497222.spf05.hubspotemail.net -all",
  "dmarc": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800",
  "dmarcPolicy": "reject",
  "subdomains": null,
  "subdomainCount": null,
  "checkedAt": "2026-10-11T15:49:10.941Z",
  "notes": []
}
```

### How much does it cost to look up DNS records?

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 DNS lookups | $0.80 | $0.64 | $0.52 | $0.40 |

You pay only for the events in the table. There is no start fee, and compute time and proxies are included.

Checking 10,000 hostnames costs $8.00 on the Free plan and $4.00 on Business. A hostname is charged once whatever the number of record types, and hostnames that no resolver answered or that changes-only mode leaves out cost nothing. A name that does not exist is charged, because the resolver did answer: its row has empty records and the NXDOMAIN note.

### Limits

- Queries go to Cloudflare's DNS over HTTPS service, with Google Public DNS as a fallback, never to the target's own nameservers. Answers can come from their cache, and TTL values are not returned.
- Up to 6 hostnames are checked at a time, each with up to 9 queries. A query waits 15 seconds and is tried twice with each resolver before the hostname is given up with the note `DNS resolvers did not answer`.
- When resolvers fail for more than half of the hostnames, the run is marked as failed.
- Reverse DNS (PTR records of IP addresses) is not supported; IP addresses in the input are skipped.
- DMARC is read only at the exact hostname. A subdomain without its own record shows `dmarc: null` even when its parent domain publishes one that mail receivers would apply.
- When a hostname is an alias, `a`, `aaaa` and `caa` hold the records of the target it points to.
- `mailProvider` names whoever receives the mail first. Companies that route mail through a filtering service show that service, such as `Proofpoint` or `Mimecast`, and in a test of 104 large company domains in October 2026, 23 matched no known provider and show `Other`.
- One run returns at most 10,000 hostnames unless you raise **Maximum rows**.

### Watching DNS for changes

Give the run a **Watch name**, or save it as a task, which keeps a snapshot automatically. Each run compares the A, MX, NS, SPF and DMARC values of every hostname with the previous run and writes the result to the `CHANGES` record: counts of new, changed and gone hostnames, and for each change the field with its old and new value. Turn on **Return only hostnames whose records changed since the last run** to get and pay for only the changed and new hostnames. AAAA, TXT records other than SPF, CAA and SOA are not compared.

Records are compared as sets, so a different order of the same records is not a change. Sites behind a content delivery network do change A addresses often: in two runs a minute apart in October 2026, 11 of 100 large company domains, such as google.com and walmart.com, had new A addresses, while none had a change in MX, NS, SPF or DMARC. For such sites, leave A out of **DNS record types** and watch MX, NS, SPF and DMARC.

### FAQ

#### Is it legal to look up DNS records?

DNS records are public by design, and the actor reads them through the documented public DNS over HTTPS services of Cloudflare and Google, without contacting the target's servers. Records describe the infrastructure of a domain. A few of them, such as SOA or a DMARC `rua` address, can contain a mailbox name, which the actor returns as published. You are responsible for how you use the data.

#### Can I check SPF and DMARC for a list of domains?

Yes. Keep **Email setup checks** on and filter the output by `spf` being empty or by `dmarcPolicy`. Of 104 large company domains checked in October 2026, 75 had a `reject` policy, 16 `quarantine`, 11 `none` and 2 no DMARC record at all.

#### Why are the registrar and expiry fields empty?

This actor reads DNS only. For registrar, creation and expiry dates use our WHOIS actors linked below, which return the same row shape with those fields filled.

### Related scrapers

- [Domain WHOIS and DNS Lookup](https://apify.com/deriverge/domain-whois-dns-lookup)
- [WHOIS Domain Lookup](https://apify.com/deriverge/whois-domain-lookup)
- [Domain Availability Checker](https://apify.com/deriverge/domain-availability-checker)
- [Tech Stack Detector](https://apify.com/deriverge/tech-stack-detector)

### Support

This actor is built and maintained by deriverge s.r.o., a software company based in the Czech Republic. If a run fails or a field you need is missing, please open an issue in the **Issues** tab or write to us at info@deriverge.com. We respond in English and Czech. Runs can be scheduled in Apify Console or started from the **API** tab, which has examples for Python, JavaScript and cURL and works with Make, Zapier, n8n and the Apify MCP server. If the actor saves you time, a short review helps other people find it.

# Changelog

This Actor's version history is a separate document: https://apify.com/deriverge/dns-records-lookup/changelog.md

# Actor input Schema

## `domains` (type: `array`):

Hostnames or domain names, also as URLs or email addresses. Records are read for the hostname exactly as given, so www.example.com and example.com are two different lookups.

## `dnsTypes` (type: `array`):

Which record types to read. Leave empty for all eight.

## `emailChecks` (type: `boolean`):

Read the SPF record, the DMARC record and policy, and name the mail provider from the MX hosts. Two extra DNS queries per domain, no extra charge.

## `changesOnly` (type: `boolean`):

Keeps a snapshot per watch name (or per saved task) and returns only hostnames whose A, MX, NS, SPF or DMARC records changed, plus new hostnames. Schedule it hourly or daily and you have a DNS change monitor that bills only for what moved.

## `watchName` (type: `string`):

Name of the snapshot used by the changes-only mode, for example "our-domains". Runs from a saved task get a snapshot automatically even without a name.

## `maxItems` (type: `integer`):

Hard cap on returned hostnames in the run.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "blog.apify.com",
    "bbc.co.uk"
  ],
  "dnsTypes": [
    "A",
    "AAAA",
    "CNAME",
    "MX",
    "NS",
    "TXT",
    "SOA",
    "CAA"
  ],
  "emailChecks": true,
  "changesOnly": false,
  "maxItems": 10000
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per hostname with A, AAAA, CNAME, MX, NS, TXT, SOA and CAA records, SPF, DMARC and the mail provider.

## `changes` (type: `string`):

Domains whose registrar, expiry, statuses, nameservers or key DNS records changed, with the old and new values, compared with the previous snapshot of the same watch name or task.

## `summary` (type: `string`):

Counts, what was charged, lookups that failed and notes.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "apify.com",
        "blog.apify.com",
        "bbc.co.uk"
    ],
    "dnsTypes": [
        "A",
        "AAAA",
        "CNAME",
        "MX",
        "NS",
        "TXT",
        "SOA",
        "CAA"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/dns-records-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "domains": [
        "apify.com",
        "blog.apify.com",
        "bbc.co.uk",
    ],
    "dnsTypes": [
        "A",
        "AAAA",
        "CNAME",
        "MX",
        "NS",
        "TXT",
        "SOA",
        "CAA",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("deriverge/dns-records-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "apify.com",
    "blog.apify.com",
    "bbc.co.uk"
  ],
  "dnsTypes": [
    "A",
    "AAAA",
    "CNAME",
    "MX",
    "NS",
    "TXT",
    "SOA",
    "CAA"
  ]
}' |
apify call deriverge/dns-records-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deriverge/dns-records-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/xLQrQPvcqKynymPIY/builds/VvOBPOEMdSDp7RhhG/openapi.json
