# Bulk DNS Record Lookup: A, MX, TXT, NS, CNAME & SOA (`accountable_eel/dns-record-lookup`) Actor

Get MX and SPF records for a list of domains without API key, and see which email provider each domain uses. Check which domains publish a DMARC record and score a domain list for SPF DKIM and DMARC. A, AAAA, TXT, NS, CNAME, SOA and DNSSEC. An mxtoolbox/dig alternative online for Google Sheets.

- **URL**: https://apify.com/accountable_eel/dns-record-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 successful 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 Record Lookup

You give this actor a list of domains, and it queries their live DNS records — A,
AAAA, MX, TXT, NS, CNAME, or SOA — straight from
[Cloudflare's public DNS-over-HTTPS resolver](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/),
the same infrastructure behind `1.1.1.1`. No API key, no scraping, no zone
transfer — just the answer a DNS resolver already gives out publicly, returned
as one flat row per domain (or one row per record, if you want it that way).

### Who it's for

The accountable_eel catalogue sells company intelligence columns for outbound. Each actor takes a
list of domains or company identifiers and returns one flat, stably-named row per input —
firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an
n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and
per-domain: a few tenths of a cent for a row that was actually found, and nothing for a
miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists —
VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No
seat licence, no monthly minimum, no credit system to decode.

For DNS specifically, that means you paste a list of domains and get back what each one's
nameservers are actually publishing right now — mail routing, IP addresses, TXT-record policy,
DNSSEC status — without opening a terminal or writing a resolver loop yourself.

### Why this one

- **Queries a real resolver, not a scrape.** Every lookup goes to Cloudflare's public
  DNS-over-HTTPS endpoint over HTTPS — the same service that answers `1.1.1.1` — so there's
  nothing brittle to break when a target site changes its HTML.
- **All seven record types from one actor.** A, AAAA, MX, TXT, NS, CNAME, SOA — pick per domain
  by appending `:TYPE` to the input, instead of running seven separate tools.
- **DNSSEC and recursion signal on every row.** `dnssecValidated` and `recursionAvailable` come
  back with every lookup, useful for a security posture pass across a domain list without a
  separate DNSSEC checker.
- **Misses are free.** NXDOMAIN, SERVFAIL, and a NOERROR response with zero answer records are
  all classified as not-found and never billed — you only pay for a domain that actually
  resolved.
- **Row shape you choose.** `expandRows` gives you one row per DNS record (handy for a
  spreadsheet or a Clay table) or one row per domain with all its records nested in an array —
  either way, you're billed once per domain, not per record.

### What you get

Every row carries these fields. `domain` through `dnssecValidated` are the DNS-specific fields
(pulled straight from `.actor/profile.json`); the rest are the same envelope every actor in this
catalogue uses.

| Field | Type | Description |
|---|---|---|
| `query` | string | The input exactly as given — `example.com` or `example.com:MX` |
| `found` | boolean | `true` if the domain resolved for the queried type with at least one answer record |
| `status` | string | `"OK"` on a found row; `NOT_FOUND`, `BLOCKED`, `REQUEST_FAILED`, or `BAD_FORMAT` on a miss |
| `domain` | string | The queried domain, lowercased |
| `queriedType` | string | The DNS record type that was queried: `A`, `AAAA`, `MX`, `TXT`, `NS`, `CNAME`, or `SOA` |
| `records` | array | Every answer record Cloudflare returned for that type — `name`, `type` (numeric DNS type), `ttl`, and `data` per record |
| `recursionAvailable` | boolean | The resolver's `RA` flag — whether recursive lookups were available for this query |
| `dnssecValidated` | boolean | The resolver's `AD` (Authenticated Data) flag — `true` only when the response chain was cryptographically validated |
| `scrapedAt` | string (ISO 8601) | Timestamp of the lookup |
| `message` | string | Present only on miss rows — a human-readable reason (e.g. why the domain didn't resolve) |

With `expandRows` on (the default), each record in `records` becomes its own dataset row instead
of a nested array — same fields, one DNS record per row. If any `data` field name were to collide
with a reserved envelope key (`query`, `found`, `status`, `message`, `scrapedAt`), it would be
renamed to `<key>Value` in the output — that doesn't happen with this actor's current fields, but
it's the rule the shared harness applies across the whole catalogue so column names never
silently overwrite each other.

### Email security columns

Turn a domain list into an SPF DKIM DMARC check for a list of domains, with no separate email
step. These columns are on by default (`emailSecurity`) and are computed from the same
DNS-over-HTTPS resolver as everything else — an MX lookup for the email provider by domain, plus
a TXT lookup on the domain itself for SPF and on `_dmarc.<domain>` for DMARC. When the record type
you already queried happens to be MX or TXT, that answer is reused instead of firing a second
request for the same name.

| Field | Type | Description |
|---|---|---|
| `emailProvider` | string | `google`, `microsoft`, `zoho`, `proton`, `other`, or `none` — read off the domain's MX hosts |
| `hasSpf` | boolean | Whether the domain has a `v=spf1` TXT record |
| `spfPolicy` | string | `-all`, `~all`, `?all`, or `none` — the qualifier on the SPF record's `all` mechanism. `null` when there's no SPF record at all |
| `hasDmarc` | boolean | Whether `_dmarc.<domain>` has a `v=DMARC1` TXT record |
| `dmarcPolicy` | string | `none`, `quarantine`, or `reject` — the DMARC record's `p=` tag. `null` when there's no DMARC record at all |
| `hasDkim` | boolean | `true`/`false` only when **Also check DKIM** (`checkDkim`) is on; `null` when DKIM wasn't probed for this row |
| `mailSecurityScore` | number | 0-100, see the formula below. `null` when email security columns are off |

**`mailSecurityScore` formula** — points are only ever awarded for something this actor actually
checked; nothing here is guessed:

| Signal | Points |
|---|---|
| `emailProvider` is not `none` (the domain has a mail route at all) | 10 |
| `hasSpf` | 25, +10 more for `spfPolicy: "-all"`, or +5 for `"~all"` |
| `hasDmarc` | 20, +15 more for `dmarcPolicy: "reject"`, or +8 for `"quarantine"` |
| `hasDkim` (only if `checkDkim` was on for this run) | 20 |

A domain with hard-fail SPF, a reject DMARC policy, and DKIM confirmed on a checked selector
scores 100; an unconfigured domain scores 0. **The score never counts DKIM unless you turned
`checkDkim` on** — a real domain with real DKIM will score up to 20 points lower than its true
posture if you leave DKIM unchecked, rather than this actor guessing at a signal it never looked
for.

**About `hasDkim`.** DKIM has no fixed, discoverable location in DNS — the record lives at
`<selector>._domainkey.<domain>`, and the selector is chosen by whichever mail platform sends the
mail, with no DNS mechanism to enumerate it. Turning on **Also check DKIM** probes a short list of
selectors real providers actually use (`google`, `selector1`, `selector2`, `default`, `k1`,
`mail`, `s1`) and reports a hit. `hasDkim: false` means none of those specific selectors resolved
— **not** that the domain has no DKIM at all; `hasDkim: null` means it wasn't checked (the
default). This is the same best-effort DKIM check, on the same list of selectors, that
[Email Deliverability Check](https://apify.com/accountable_eel/email-deliverability-check) runs
for a list of email addresses instead of domains.

Turn `emailSecurity` off if you only want the raw DNS record type you queried, with no extra
lookups. Billing is unchanged either way — these columns never add a second per-domain charge, the
same `item-lookup` event covers the whole row.

### Run it on a schedule

DNS records change — a mail migration, a new TXT policy, a DNSSEC rollout — and a single run only
tells you what's true right now. Point this actor at the same `domains` list on a cadence and each
run gives you a fresh snapshot every day (or whatever interval you pick), so a change shows up the
next time the schedule fires instead of going unnoticed.

Schedule it in Apify (Schedules > Create) or trigger it from n8n/Make; each run is a fresh query,
billed the same per-resolved-domain rate as any other run.

### Price

$2 per 1,000 domains, plus a $0.00005 start fee. Misses (`found:false`) are never charged.

That's $0.002 per resolved domain at the FREE tier, dropping to $0.001 per resolved domain on
paid tiers — the $0.00005 actor-start charge is fixed regardless of tier. 1,000 domains through
this actor: **~$2** if every one resolves, less for any that come back NXDOMAIN or SERVFAIL. The
same 1,000 rows through a credit-based enrichment platform: **$80–$400**. A domain that doesn't
resolve for the queried type costs you nothing either way — you still get a row (`found: false`),
you just aren't billed for it.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `domains` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~dns-record-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"domains":["github.com"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

1) Paste a list of domains into the input, one per line. `github.com` on its own queries the `A`
   record by default.
2) To query a different record type, append `:TYPE` — `github.com:MX`, `cloudflare.com:TXT`,
   `example.com:SOA`. Want two types for the same domain? List it twice, once per type — each is
   billed independently.
3) Turn on **Try it first** (`testRun`) to run just the first 5 items and check the shape of the
   output before committing to a full list.
4) Turn on **Hide rows with no result** (`onlyFound`) if you only want rows that actually
   resolved in your dataset — misses are free either way, this just keeps them out of the export.
5) Leave `maxConcurrency` at its default of 5 unless you have a reason to push it — this target
   has no browser fallback, so a burst of parallel requests risks getting rate-limited rather than
   finishing faster.
6) Run it, then export the dataset as CSV/JSON or pull it via the API into whatever pulls your
   enrichment data — Clay, n8n, a script, or an agent.

**Bulk example.** A 25-domain input covering major tech, ecommerce, and SaaS domains:

```json
{
  "domains": [
    "github.com", "stripe.com", "shopify.com", "cloudflare.com", "wordpress.org",
    "nike.com", "target.com", "airbnb.com", "spotify.com", "netflix.com",
    "salesforce.com", "hubspot.com", "mailchimp.com", "wix.com", "squarespace.com",
    "bigcommerce.com", "walmart.com", "ebay.com", "zendesk.com", "intercom.com",
    "hootsuite.com", "atlassian.com", "dropbox.com", "slack.com", "zoom.us"
  ]
}
```

25 domains ≈ **$0.05** if every one resolves ($0.002 per resolved domain at the FREE tier, plus
the $0.00005 actor-start charge) — less for any that come back NXDOMAIN or SERVFAIL, since misses
are never billed.

### Input

```json
{
  "domains": [
    "github.com"
  ]
}
```

One domain per line. Defaults to an A record lookup; append :TYPE (A, AAAA, MX, TXT, NS, CNAME, or SOA) to query a different record type, e.g. github.com:MX. Accepted formats: github.com, github.com:MX.

```json
{
  "items": ["github.com", "github.com:MX", "cloudflare.com:TXT"],
  "maxConcurrency": 5
}
```

`items` is a list of domains to query. By default each domain is queried for its `A`
record; append `:TYPE` (one of `A`, `AAAA`, `MX`, `TXT`, `NS`, `CNAME`, `SOA`) to query a
different record type, e.g. `github.com:MX`. One dataset row is returned per item; rows
with `"found": false` are never charged. `maxConcurrency` controls how many DNS queries
run in parallel — this target has no browser fallback, so keeping it conservative avoids
getting rate-limited.

### Sample output

| query | found | status | domain | queriedType | records | recursionAvailable | dnssecValidated | emailProvider | hasSpf | spfPolicy | hasDmarc | dmarcPolicy | hasDkim | mailSecurityScore | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| github.com | true | OK | github.com | A | <dns records> | true | false | <email provider> | <has spf record> | <spf policy> | <has dmarc record> | <dmarc policy> | \<has dkim record (checked selectors)> | \<mail security score (0-100)> | 2026-09-23T06:00:53.644Z |

One row per item, for example:

```json
{
  "query": "github.com:MX",
  "found": true,
  "data": {
    "domain": "github.com",
    "queriedType": "MX",
    "records": [
      { "name": "github.com", "type": 15, "ttl": 3600, "data": "1 aspmx.l.google.com." }
    ],
    "recursionAvailable": true,
    "dnssecValidated": false
  },
  "scrapedAt": "2026-08-21T10:00:00.000Z"
}
```

Domains that don't resolve for the queried type — NXDOMAIN, SERVFAIL, or a NOERROR
response with no answer records — still get a row (`"found": false`), so you always get
one row per input, but you're never charged for those.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~dns-record-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"domains":["github.com"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~dns-record-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"domains":["github.com"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~dns-record-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"domains":["{{domain}}"]}`, mapping the row's domain into the `domains` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "DNS Lookup Tool: Bulk A, MX, TXT & NS Records, No API Key" — the agent will find and run this actor.

### Tips

- **Query more than one record type for the same domain by listing it twice** —
  `example.com:A` and `example.com:MX` as separate input items. Each is billed
  independently as its own lookup, so you only pay for the types you actually ask for.
- **`dnssecValidated: false` doesn't mean "no DNSSEC."** It reflects Cloudflare's `AD` flag for
  that one query — `true` only when the resolver could cryptographically validate the response
  chain. A `false` can mean DNSSEC isn't configured, or just that validation wasn't attempted for
  that lookup. If you need a definitive DNSSEC posture, cross-check a `false` against the domain's
  `DNSKEY`/`DS` records directly.
- **Turn `expandRows` off if you want one row per domain.** With multiple `MX` or `TXT` records,
  the default (on) gives you one row per record — cleaner for filtering, but it multiplies row
  count on domains with a lot of records (still billed once per domain either way).
- **Keep `maxConcurrency` at 5 for large batches.** This target has no browser fallback to fall
  back on if Cloudflare starts throttling you, so slow-and-steady finishes a 10,000-domain list
  more reliably than a burst that gets rate-limited partway through.
- **Use `MX` and `TXT` together for a quick deliverability read** before a cold outreach or
  domain migration — `MX` tells you where mail routes, `TXT` surfaces SPF/DMARC records (look for
  `v=spf1` or `v=DMARC1` in the `data` field).
- **Resolve `A`/`AAAA`/`CNAME` across a domain list for infrastructure discovery** — the IP or
  CNAME target tells you which host, CDN, or cloud provider a domain points to, without visiting
  the site.
- **Check `NS` and `SOA` after a DNS cutover** to confirm propagation finished — query the same
  domain before and after a migration and diff the `records` array.
- **Run `dnssecValidated` across a portfolio you manage** as a lightweight security-posture pass —
  it won't replace a dedicated DNSSEC audit, but it flags which domains are worth a closer look.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`dns-record-lookup`) | $0.002 per resolved domain (FREE tier, down to $0.001 on paid tiers), $0.00005 actor start, nothing for NXDOMAIN/SERVFAIL | One row per domain or per record — A/AAAA/MX/TXT/NS/CNAME/SOA, plus DNSSEC and recursion flags, from one API call | One resolver (Cloudflare's), one record type per query item — no zone transfer, no historical record archive |
| **DIY** (`whois`/`dig`) | Free, your time | The same records, one domain at a time from a terminal | Fine for a handful of domains; across a list of thousands you're writing your own retry logic, rate limiting, and parsing to get a flat table out of it |
| **Clay** | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |

Prices for third-party tools are their published list prices as of August 2026 and are not
tracked here — check the vendor before relying on the comparison.

### FAQ

**What happens if a domain doesn't resolve — do I still pay?**
No. NXDOMAIN, SERVFAIL, and a NOERROR response with zero answer records for the queried type are
all treated as a miss (`found: false`, with a `status` of `NOT_FOUND`, `BLOCKED`, `REQUEST_FAILED`,
or `BAD_FORMAT`). Misses still produce a dataset row so your row count matches your input count,
but they're never billed.

**Do I need a Cloudflare API key?**
No. Cloudflare's DNS-over-HTTPS resolver is free and public — this actor calls it directly, with
no credentials required on your end.

**Are there rate limits I should worry about on a large batch?**
`maxConcurrency` (default 5) controls how many DNS queries run in parallel. This target has no
browser fallback, so keep it conservative on large runs — a burst of parallel requests risks
getting throttled rather than finishing faster. A list of a few thousand domains at the default
concurrency runs without issue.

**How fresh is the data — could I get a stale record?**
Every run queries Cloudflare's resolver live, so you get whatever it's currently serving. That
said, DNS itself is cached: Cloudflare's resolver respects each record's TTL, so a record that
changed in the last few minutes (or hours, for a long-TTL `SOA` or `NS` record) may not have
propagated to the resolver yet. This actor reports what the resolver has right now, not a
guaranteed real-time read of the authoritative nameserver.

**Do I need to configure a proxy?**
No. Requests go to Cloudflare's public resolver, not to the target company's own infrastructure,
so there's nothing to spoof or rotate around. The proxy configuration field is there because the
shared actor template includes it, but DNS-over-HTTPS to Cloudflare doesn't need one.

**Is DNS record data personal data under GDPR?**
DNS records describe a domain's infrastructure — IP addresses, mail servers, nameservers, TXT
policy strings — not people. They're not personal data, and this actor doesn't process, store, or
return anything about individuals.

**Can I schedule this to run on a cadence?**
Yes, through Apify's own Scheduler — set up a recurring run against a saved input (e.g. a
maintained list of domains you monitor) and it queries fresh on each run. There's no built-in
delta/diff feature here — every run is a full lookup against the current input list, so if you
want to know what changed between runs, keep the previous dataset export and diff it yourself
against the new one, or expand `TXT`/`MX`/`NS` and compare the record values.

**Can an AI agent call this directly?**
Yes. It's registered on the Apify MCP server — a client like Claude or Cursor can find and run it
by name ("DNS Record Lookup | Apify") — or call the Apify API directly over HTTP, same as the
`curl` example above.

### Related actors

- [Domain RDAP Lookup](https://apify.com/accountable_eel/domain-rdap-lookup) — registration data
  (registrar, creation/expiry dates, nameservers) for the same domain list, from RDAP instead of
  DNS.
- [Email Deliverability Check](https://apify.com/accountable_eel/email-deliverability-check) —
  mail route, disposable/role detection, and provider fingerprint for a domain, building on the
  same `MX`/`TXT` signal this actor surfaces raw.
- [Tech Stack Lookup](https://apify.com/accountable_eel/tech-stack-lookup) — what a domain is
  actually running (CMS, analytics, ecommerce platform), for when DNS tells you where it points
  but not what's there.

If this saved you a scrape, a rating on the Store page helps other buyers find it.

# Actor input Schema

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

One domain per line. Defaults to an A record lookup; append :TYPE (A, AAAA, MX, TXT, NS, CNAME, or SOA) to query a different record type, e.g. github.com:MX. Accepted formats: github.com, github.com:MX. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `emailSecurity` (type: `boolean`):

Adds emailProvider, hasSpf, spfPolicy, hasDmarc, dmarcPolicy and mailSecurityScore to every row, by looking up the domain's MX and TXT records alongside whatever record type you asked for above. Turn off to skip these extra lookups and get back only the record type you queried.

## `checkDkim` (type: `boolean`):

Off by default. When on, checks a short list of provider-default DKIM selectors (google, selector1, selector2, default, k1, mail, s1) via DNS TXT lookups. A hit confirms DKIM is set up; no hit means none of those specific selectors are in use, not that DKIM is absent entirely - DKIM has no way to enumerate selectors from DNS alone. Has no effect unless email security columns are on.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each DNS record found gets its own row instead of being grouped under its domain. You're still only charged once per domain, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "domains": [
    "github.com"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "emailSecurity": true,
  "checkDkim": false,
  "columns": [
    "domain",
    "queriedType",
    "records",
    "recursionAvailable",
    "dnssecValidated",
    "emailProvider",
    "hasSpf",
    "spfPolicy",
    "hasDmarc",
    "dmarcPolicy",
    "hasDkim",
    "mailSecurityScore"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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": [
        "github.com"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/dns-record-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": ["github.com"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/dns-record-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": [
    "github.com"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/dns-record-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/dns-record-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/u90eLSbj0ajZuIFc8/builds/nRgyhhBFGq1OaTgB7/openapi.json
