# Domain Intelligence API (`asgardlabs/domain-intel-api`) Actor

Everything public about a domain in one row: registrar, created and expiry dates, name servers, DNS records, mail and DNS provider, SPF, DMARC and DKIM state, TLS certificate days left, site status. Protocol lookups only, no scraping. A third of a cent per domain; unregistered or dead ones free.

- **URL**: https://apify.com/asgardlabs/domain-intel-api.md
- **Developed by:** [Asgard Labs](https://apify.com/asgardlabs) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 domains

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Domain Intelligence API – WHOIS/RDAP, DNS, SSL, SPF, DMARC in one row

Give it domain names, URLs or email addresses. Get one row per domain with everything a registry, a name server and a TLS handshake will tell you: who the registrar is, when the domain was created and when it expires, which name servers and mail provider it uses, whether SPF, DMARC and DKIM are set up properly, what certificate the site presents and how many days it has left, and whether the website answers.

No scraping, no browser, no proxies, no API keys. Every fact comes from the protocol that owns it: RDAP for registration (the successor of WHOIS), DNS for records, a direct TLS handshake for the certificate, one HTTPS request for the site.

### Domain data per row

- **Registration** – `registrar`, `registrarIanaId`, `createdAt`, `updatedAt`, `expiresAt`, `ageDays`, `expiresInDays`, `status` (EPP codes such as `client transfer prohibited`), `dnssec`, `nameservers`, `registrant` and `admin` where the registry or registrar publishes them, `abuseEmail`
- **DNS** – `dns.a`, `dns.aaaa`, `dns.mx`, `dns.ns`, `dns.cname`, `dns.soa`, `dns.caa`; derived `dnsProvider` (Cloudflare, Route 53, GoDaddy, …) and `mailProvider` (Google Workspace, Microsoft 365, Zoho, Proton, …)
- **Email security** – `email.spf` (record, `all` qualifier, includes, lookup count, problems), `email.dmarc` (policy, pct, rua/ruf, problems), `email.dkim` (selectors found), `mtaSts`, `bimi`, `receivesMail`, `spoofable`, `emailGrade` A–F; flat `dmarcPolicy` and `spfAll` for filtering
- **TLS** – `tls.issuer`, `validFrom`, `validTo`, `daysLeft`, `sans`, `wildcard`, `trusted`, `hostnameMatch`, `selfSigned`, `protocol`; flat `certIssuer`, `certDaysLeft`
- **Website** – `web.status`, `finalUrl`, `server`, `title`; flat `httpStatus`
- **flags** – `unregistered`, `new-domain`, `domain-expires-soon`, `domain-expired`, `on-hold`, `dnssec`, `no-spf`, `no-dmarc`, `dmarc-none`, `spoofable`, `no-mail`, `cert-expires-soon`, `cert-expired`, `cert-untrusted`, `cert-hostname-mismatch`, `no-tls`, `site-error`, `site-down`, `no-dns`
- **notes** – anything that could not be checked, in plain words (`no RDAP service for .de`, `tls: timeout`)

### Use cases

- Qualify a lead list: company age, real mail provider, whether the site is up.
- Audit a portfolio: certificates and domains that expire in the next 30 days.
- Security review: which domains can be spoofed because DMARC is missing or set to `none`.
- Enrich domains for an AI agent that needs registration and DNS facts as JSON.
- Check a list of domains for availability before buying.

### Input

| Field | Default | Meaning |
|---|---|---|
| `domains` | – | Domain names, URLs or email addresses, one per line. Different spellings of one domain are checked once and delivered once. |
| `registration` | true | RDAP registration facts. |
| `dns` | true | DNS records and providers. |
| `emailSecurity` | true | SPF, DMARC, DKIM, MTA-STS, BIMI. |
| `tls` | true | Certificate via TLS handshake on port 443. |
| `web` | true | One HTTPS request: status, final URL, title. |
| `dkimSelectors` | 16 common | Selector names to try under `_domainkey`. |
| `includeRaw` | false | Attach the raw RDAP document and all TXT records. |
| `timeoutSecs` | 20 | Time budget per domain across all lookups. |
| `concurrency` | 10 | Domains checked in parallel. |

### Output example

```json
{ "domain": "shopify.com", "ok": true, "registered": true,
  "registrar": "MarkMonitor Inc.", "createdAt": "2005-03-11T05:18:03Z", "expiresAt": "2027-03-11T05:18:03Z", "ageDays": 7861, "expiresInDays": 174,
  "status": ["client transfer prohibited", "server delete prohibited"], "dnssec": false, "nameservers": ["gold.foundationdns.com", "gold.foundationdns.net"],
  "dnsProvider": "Foundation DNS", "mailProvider": "Google Workspace",
  "dns": { "a": ["23.227.38.33"], "mx": [{ "host": "aspmx.l.google.com", "priority": 1 }], "ns": ["gold.foundationdns.com"], "caa": [] },
  "email": { "spf": { "present": true, "all": "~all", "includes": ["_spf.google.com", "sendgrid.net"], "lookups": 3, "problems": [] },
             "dmarc": { "present": true, "policy": "reject", "pct": 100, "rua": ["dmarc-aggregate@shopify.com"], "problems": [] },
             "dkim": { "found": ["google", "mail"] }, "receivesMail": true, "spoofable": false, "grade": "A" },
  "dmarcPolicy": "reject", "spfAll": "~all", "emailGrade": "A",
  "tls": { "ok": true, "issuer": "Google Trust Services", "validTo": "2026-11-08T19:37:54.000Z", "daysLeft": 51, "trusted": true, "hostnameMatch": true, "protocol": "TLSv1.3" },
  "certIssuer": "Google Trust Services", "certDaysLeft": 51,
  "web": { "ok": true, "status": 200, "finalUrl": "https://www.shopify.com/", "server": "cloudflare", "title": "Shopify: The All-in-One Commerce Platform" },
  "httpStatus": 200, "flags": [], "notes": [], "durationMs": 3187 }
```

### How to use

1. Paste your domains, one per line. The example input is ready to run as is.
2. Click **Start**. Rows appear in the **Output** tab as they are found.
3. Filter on `flags`, `expiresInDays`, `certDaysLeft`, `dmarcPolicy` or `mailProvider`, then download as CSV, JSON or Excel.
4. For monitoring, create a **Schedule** with your portfolio and watch the `domain-expires-soon` and `cert-expires-soon` flags.

### Pricing

Pay per event: **`domain` $0.003** per domain that returned registration facts or DNS records. A 1,000-domain list costs $3. Domains that are not registered, do not resolve, or could not be reached are delivered with a plain reason and not charged.

### Good to know

- **Registrant details are usually redacted.** Since 2018 most registries publish `REDACTED FOR PRIVACY` for names and emails. The registrar, dates, status and name servers are always there.
- **Some registries have no RDAP service** (.de, .io, .ch, .lk and others). Those rows still get DNS, email, TLS and website facts, and `notes` says `no RDAP service for .de`.
- **Country-code second levels are handled**: `shop.example.co.uk` is checked as `example.co.uk`; `www.gov.uk` is its own registered name.
- **DKIM can only be confirmed for selectors that are tried.** The default 16 cover Google, Microsoft, Amazon SES, Zoho, Mailchimp, SendGrid and Postmark; add your own in `dkimSelectors`.
- **`spoofable`** means the domain receives mail but has no enforcing DMARC policy (`quarantine` or `reject`).
- **Errors are in plain words**: `not registered`, `timeout`, `rdap: rate limited (429)`, `tls: ECONNREFUSED (connection refused)`.

### Use from code or an AI agent

Every run can be started and read through the Apify API, the JavaScript and Python clients, or an AI agent with tool access. Open the **API** tab on this page for ready-made snippets. Pricing is per domain delivered, so an agent pays only for what it receives.

### FAQ

**Is this WHOIS?**
It is RDAP, the structured successor that registries now serve instead of WHOIS text. Same facts, machine-readable, with the registrar's own record fetched when it adds the registrant.

**Why does a domain show `registered: true` but no registrar?**
Its registry has no RDAP service, so registration facts are not available; `registered` was concluded from DNS. `notes` says so.

**Can I check availability for buying?**
Yes. Unregistered domains come back with `registered: false` and the `unregistered` flag, free of charge.

**Which TLS certificate is read?**
The one the host presents on port 443 with SNI for the exact hostname you gave (`www.example.com` if that is what you pasted, otherwise the domain).

**How is the email grade computed?**
One point each for a valid SPF without `+all`, a valid DMARC record, an enforcing DMARC policy, and at least one DKIM selector found. A = 4 points, F = 0.

# Actor input Schema

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

Domain names, URLs or email addresses, one per line. "https://www.shop.example.co.uk/x" and "jane@example.co.uk" both mean example.co.uk. Up to thousands per run.

## `registration` (type: `boolean`):

Registrar, creation, update and expiry dates, status codes, DNSSEC, registrant when the registry publishes it. Registries without an RDAP service (for example .de, .io, .ch) are reported as such, not as an error.

## `dns` (type: `boolean`):

A, AAAA, MX, NS, TXT, CNAME, SOA and CAA records, plus the DNS and mail provider derived from them.

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

Parses the SPF and DMARC records and checks common DKIM selectors. Needs DNS records on.

## `tls` (type: `boolean`):

Issuer, validity dates, days left, hostname match, alternative names, protocol, from a direct TLS handshake on port 443.

## `web` (type: `boolean`):

One HTTPS request to the domain: status code, final URL after redirects, server header and page title.

## `dkimSelectors` (type: `array`):

Selector names looked up as <selector>.\_domainkey.<domain>. The defaults cover Google, Microsoft, Amazon SES, Mailchimp, SendGrid, Zoho and generic setups.

## `includeRaw` (type: `boolean`):

Attach the raw RDAP document and every TXT record to each row. Rows get larger.

## `timeoutSecs` (type: `integer`):

Time budget per domain across all lookups. Slow registries are reported as timed out for that part only.

## `concurrency` (type: `integer`):

How many domains to check at the same time. Registries rate-limit around 10 requests per second per client.

## Actor input object example

```json
{
  "domains": [
    "shopify.com",
    "apify.com",
    "wikipedia.org",
    "this-domain-is-not-registered-2026.com"
  ],
  "registration": true,
  "dns": true,
  "emailSecurity": true,
  "tls": true,
  "web": true,
  "dkimSelectors": [
    "google",
    "selector1",
    "selector2",
    "default",
    "k1",
    "k2",
    "s1",
    "s2",
    "mail",
    "dkim",
    "zoho",
    "amazonses",
    "mandrill",
    "sendgrid",
    "pm",
    "protonmail"
  ],
  "includeRaw": false,
  "timeoutSecs": 20,
  "concurrency": 10
}
```

# Actor output Schema

## `domains` (type: `string`):

One row per domain: registration facts, DNS records, providers, SPF/DMARC/DKIM state, TLS certificate, website status, flags, and a plain-words error when something could not be checked.

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

Counts per run (checked, registered, unregistered, charged, failed), errors with plain reasons, charge-limit state.

# 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": [
        "shopify.com",
        "apify.com",
        "wikipedia.org",
        "this-domain-is-not-registered-2026.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("asgardlabs/domain-intel-api").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": [
        "shopify.com",
        "apify.com",
        "wikipedia.org",
        "this-domain-is-not-registered-2026.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("asgardlabs/domain-intel-api").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": [
    "shopify.com",
    "apify.com",
    "wikipedia.org",
    "this-domain-is-not-registered-2026.com"
  ]
}' |
apify call asgardlabs/domain-intel-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,asgardlabs/domain-intel-api"
        }
    }
}
```

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/oYi4XOauAO8vKTSlP/builds/HbYDWLcxCdQI1ft25/openapi.json
