# Domain Intelligence | $0.02 / domain | (`glasswing/domain-intelligence-report`) Actor

One audit-ready row per domain: DNS records, SPF, DMARC and DKIM, mail provider, TLS expiry, registrar, hosting IP, ASN, CDN and detected tech stack. No API keys needed. Export to CSV, JSON or Excel.

- **URL**: https://apify.com/glasswing/domain-intelligence-report.md
- **Developed by:** [Raffy](https://apify.com/glasswing) (community)
- **Categories:** Developer tools, SEO tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Domain Intelligence do? 🔎

**Paste a list of domains, get one audit-ready row per domain: who hosts it, what it is built with, whether its email can be spoofed, and when its TLS certificate and its registration expire.** Security teams use it to find domains with a weak DMARC policy, agencies use it to qualify leads by tech stack, and SRE teams use it to catch certificates before they lapse.

One run replaces a stack of one-domain-at-a-time tools: **check DMARC and SPF records** in bulk, run a **domain tech stack lookup**, read MX records and name the mail provider, check **SSL certificate expiry**, look up **hosting ASN and CDN**, and pull **RDAP registration data** (the modern replacement for WHOIS). No API key, no browser, no proxies — the Actor speaks DNS-over-HTTPS, RDAP, TLS and plain HTTPS, so a domain takes a couple of seconds and costs $0.05.

✨ **Try it:** hit **Start** with the prefilled domains. Three full reports land in about 10 seconds.

🛡️ **Honest by design:** every value is measured, never guessed. An unrecognised platform is `null`, not "probably WordPress". A row that could not be checked says so in `status` and `error`, and you are not billed for it.

### Use cases: spoofing audits, tech-stack lead scoring, certificate monitoring 💡

- 🛡️ **Security and IT teams auditing email spoofing risk.** Run your supplier or subsidiary list and pull out every domain sitting on `p=none`, missing an SPF `-all`, or publishing no DMARC record at all — the domains an attacker can spoof today.
- 🎯 **Agencies and SDRs qualifying leads by tech stack.** Score a prospect list before a rep touches it: "every store on this list running a hosted commerce platform", "every prospect still on a self-hosted CMS", "everyone using a chat widget we integrate with".
- 🔐 **SRE and platform teams watching certificate expiry.** Schedule a daily run across your whole domain portfolio and alert when `tlsDaysToExpiry` drops under 30 or when a chain stops being trusted. The RDAP `expiresAt` date catches domains about to lapse too.
- 🏗️ **Agencies inheriting client estates.** Inventory the CMS, CDN, DNS provider, mail host and registrar of every domain a new client owns in one pass, instead of clicking through five lookup sites per domain.
- 📈 **SEO and content teams checking AI-crawler policy.** See which competitors publish an `llms.txt`, and which ones disallow GPTBot, ClaudeBot, CCBot or Google-Extended in robots.txt.
- 🔬 **Analysts mapping a market's technology.** Which frameworks, analytics, payment and hosting stacks the domains in a niche use, and which ASNs they sit behind — a whole category profiled in one dataset.

The Actor does not log in anywhere, does not read private data and does not need a captcha solver. Every signal is public protocol data that the domain owner publishes on purpose.

### What data you get 📦

One row per domain. These flat columns are what the **Overview** table shows first, and they are what most people export:

| Field | Type | What it tells you |
|---|---|---|
| `domain` | string | The registrable host name that was analysed: lower-case, punycode, no leading `www.`. |
| `cms` | string | null | Primary CMS, hosted commerce platform or site generator detected on the homepage. `null` when no fingerprint matched. |
| `cdn` | string | null | CDN or edge network in front of the site, taken from response headers first and the hosting ASN second. |
| `mailProvider` | string | null | Hosted mail provider derived from MX, `other` when MX exists but is self-hosted or unknown, `null` when the domain receives no mail. |
| `dmarcPolicy` | string | null | `none`, `quarantine` or `reject`. `null` when no DMARC record is published — the highest-risk case. |
| `tlsDaysToExpiry` | integer | null | Whole days until the TLS certificate expires; negative when it already has. |
| `registrar` | string | null | Sponsoring registrar, from RDAP. |
| `status` | string | `ok`, `not_found` or `error` (see the table below). |
| `error` | string | Why the row is not `ok`. Absent on `ok` rows. |
| `url` | string | Homepage URL of the domain (`https://<domain>/`). |
| `scrapedAt` | string | ISO 8601 timestamp of the probe. |

Each flat column is a copy of one value inside a nested block. The blocks carry the full detail:

| Block | Contents |
|---|---|
| `dns` | `a`, `aaaa` (IP arrays), `mx` (`priority` + `exchange`, sorted), `ns`, `txt` (raw TXT strings, including SaaS verification tokens), `caa` (`flags`, `tag`, `value`). |
| `emailSecurity` | `spf` (`present`, `record`, `allMechanism` — `-all`/`~all`/`?all`/`+all`, `includes`), `dmarc` (`present`, `record`, `policy`, `subdomainPolicy`, `percentage`, `reportingEnabled`), `dkimSelectorsFound` (of `google`, `selector1`, `selector2`, `k1`, `mail`, `default`). |
| `tls` | `issuer`, `issuerCn`, `subject`, `validFrom`, `validTo`, `daysToExpiry`, `sanCount`, `protocol`, `trusted`, `host` (apex or `www` fallback). |
| `rdap` | `registrar`, `registrarIanaId`, `createdAt`, `updatedAt`, `expiresAt`, `nameservers`, `status` (EPP flags such as `client transfer prohibited`), `source` (the RDAP server that answered). |
| `hosting` | `ip` (first A record), `asn`, `asnName`, `cdn`, `dnsProvider` (from the NS host names). |
| `techStack` | `server`, `poweredBy`, `cms`, `analytics[]`, `frameworks[]`, `ecommerce[]`, `chatWidgets[]`, `other[]` (marketing, payments, hosting), plus `homepageStatus`, `homepageUrl` and `title`. |
| `files` | `robotsTxt`, `llmsTxt`, `securityTxt`, `sitemap` (booleans), `sitemapUrls` (from robots.txt, max 10) and `aiBotsBlocked` (AI crawlers fully disallowed). |
| `warnings` | Non-fatal problems on an otherwise good row, e.g. `RDAP: the .de registry publishes no RDAP service`. Absent when everything worked. |

A section you switch **off** in the input is absent from the row. A section that was checked but unavailable is **`null`**, and `warnings` says why. That difference is deliberate: `null` always means "we looked and there is nothing", never "we did not look".

#### Result status (tri-state output)

| `status` | Meaning | Billed? |
|---|---|---|
| `ok` | The domain resolves and was probed. Individual sections may still be `null` with a `warnings` entry. | Yes |
| `not_found` | NXDOMAIN — the domain does not exist in DNS (unregistered, expired or a typo). | No |
| `error` | The lookups themselves failed (resolvers unreachable, broken zone/SERVFAIL, or an unparseable input). `error` says which. | No |

### Output 📤

Export as JSON, CSV, Excel, XML or HTML, or read the dataset straight from the API. This is a real row from the default run (`apify.com`, 2026-09-08), with a few repetitive `txt`, `aaaa` and `caa` entries trimmed so it stays readable:

```json
{
    "domain": "apify.com",
    "url": "https://apify.com/",
    "status": "ok",
    "scrapedAt": "2026-09-08T10:42:48.227Z",
    "dns": {
        "a": ["65.9.46.39", "65.9.46.103", "65.9.46.99", "65.9.46.26"],
        "aaaa": ["2600:9000:2094:a400:9:a03e:6540:93a1", "2600:9000:2094:6200:9:a03e:6540:93a1"],
        "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": [
            "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all",
            "google-site-verification=jrECJwBG9eeO_FUrOMhp7JY1oF8JSBxhzmLsLGvrt9s",
            "openai-domain-verification=dv-WTM7Nna92Owwh4sgmDOVUyyt",
            "zapier-domain-verification-challenge=2c358aa9-fa36-419f-b21a-ca9eb6f2791d",
            "mixpanel-domain-verify=44615d9c-27f2-4587-a781-8a0a1c734e9a"
        ],
        "caa": [
            { "flags": 0, "tag": "issue", "value": "letsencrypt.org" },
            { "flags": 0, "tag": "issue", "value": "amazonaws.com" },
            { "flags": 0, "tag": "issue", "value": "digicert.com" }
        ]
    },
    "emailSecurity": {
        "spf": {
            "present": true,
            "record": "v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all",
            "allMechanism": "-all",
            "includes": ["_spf.google.com", "mailgun.org", "amazonses.com"]
        },
        "dmarc": {
            "present": true,
            "record": "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800",
            "policy": "reject",
            "subdomainPolicy": "reject",
            "percentage": 100,
            "reportingEnabled": true
        },
        "dkimSelectorsFound": ["google"]
    },
    "mailProvider": "Google Workspace",
    "tls": {
        "issuer": "Amazon",
        "issuerCn": "Amazon RSA 2048 M04",
        "subject": "*.apify.com",
        "validFrom": "2026-07-03T00:00:00.000Z",
        "validTo": "2027-01-16T23:59:59.000Z",
        "daysToExpiry": 130,
        "sanCount": 4,
        "protocol": "TLSv1.3",
        "trusted": true,
        "host": "apify.com"
    },
    "rdap": {
        "registrar": "Amazon Registrar, Inc.",
        "registrarIanaId": "468",
        "createdAt": "2009-06-02T17:14:10Z",
        "updatedAt": "2026-05-16T16:53:04Z",
        "expiresAt": "2035-06-02T17:14:10Z",
        "nameservers": ["ns-1225.awsdns-25.org", "ns-1928.awsdns-49.co.uk", "ns-449.awsdns-56.com", "ns-839.awsdns-40.net"],
        "status": ["client transfer prohibited"],
        "source": "rdap.verisign.com"
    },
    "hosting": {
        "ip": "65.9.46.39",
        "asn": 16509,
        "asnName": "AMAZON-02 - Amazon.com, Inc.",
        "cdn": "Amazon CloudFront",
        "dnsProvider": "Amazon Route 53"
    },
    "techStack": {
        "server": null,
        "poweredBy": null,
        "cms": "Next.js",
        "analytics": ["Google Tag Manager"],
        "frameworks": ["Next.js", "React", "Bootstrap"],
        "ecommerce": [],
        "chatWidgets": ["Intercom"],
        "other": ["HubSpot", "OneTrust", "Amazon CloudFront"],
        "homepageStatus": 200,
        "homepageUrl": "https://apify.com/",
        "title": "Apify: The largest marketplace of trusted tools for AI"
    },
    "files": {
        "robotsTxt": true,
        "llmsTxt": true,
        "securityTxt": true,
        "sitemap": true,
        "sitemapUrls": ["https://apify.com/sitemap.xml"],
        "aiBotsBlocked": []
    },
    "registrar": "Amazon Registrar, Inc.",
    "cms": "Next.js",
    "cdn": "Amazon CloudFront",
    "dmarcPolicy": "reject",
    "tlsDaysToExpiry": 130
}
```

The **Overview** table of that same run, one line per domain:

| `domain` | `cms` | `cdn` | `mailProvider` | `dmarcPolicy` | `tlsDaysToExpiry` | `registrar` | `status` |
|---|---|---|---|---|---|---|---|
| apify.com | Next.js | Amazon CloudFront | Google Workspace | reject | 130 | Amazon Registrar, Inc. | ok |
| wordpress.org | WordPress | *null* | other | reject | 45 | MarkMonitor Inc. | ok |
| nextjs.org | Next.js | Vercel | Google Workspace | quarantine | 42 | Tucows Domains Inc. | ok |

And the two free row types, so you can see exactly what a domain that costs nothing looks like:

```json
[
    {
        "domain": "invalid.invalid",
        "inputDomain": "not a domain",
        "url": "https://invalid.invalid/",
        "status": "error",
        "error": "Invalid domain name: \"not a domain\" is not a host name (expected something like example.com)",
        "scrapedAt": "2026-09-08T10:43:22.234Z"
    },
    {
        "domain": "this-domain-does-not-exist-9x8y7z6w.com",
        "url": "https://this-domain-does-not-exist-9x8y7z6w.com/",
        "status": "not_found",
        "error": "NXDOMAIN: this-domain-does-not-exist-9x8y7z6w.com does not exist in DNS",
        "scrapedAt": "2026-09-08T10:43:22.526Z"
    }
]
```

### How to check DMARC, SPF and the tech stack of a domain list 🛠️

1. **Add your domains** 📝 — paste them into **Domains**, one per line. Bare names (`stripe.com`), full URLs (`https://www.stripe.com/pricing`) and e-mail addresses (`sales@stripe.com`) all work; everything is reduced to the registrable host name and duplicates are dropped.
2. **Pick the sections** ✅ — all five are on by default. Switching one off makes the run slightly faster; the price per domain does not change.
3. **Set Maximum results** 🔢 — the safety cap on how many rows the run may write.
4. **Start** ▶️ — rows appear in the dataset in real time, one per domain, roughly 1-3 seconds each with 10 domains in flight.
5. **Export or automate** 📤 — download from the **Output** tab, or add a **Schedule** so the same list is re-checked every morning.

### Input 📥

Every field, its default and when you would change it:

| Field | Type | Default | When to change it |
|---|---|---|---|
| `startUrls` | array of strings | 3 sample domains | Always — this is your domain list (the **Domains** field). Names, URLs or e-mail addresses; each entry produces exactly one row. `domains` is accepted as an alias if you prefer that name from the API. |
| `maxItems` | integer | `20` | Raise it before a production run: it caps how many rows (any status) the run may write, so it is your hard stop on volume. |
| `includeDns` | boolean | `true` | Turn off when you only want the tech stack or the certificate and do not need A, AAAA, MX, NS, TXT and CAA records. |
| `includeEmailSecurity` | boolean | `true` | Leave on for any spoofing or deliverability audit — it fills `emailSecurity`, `mailProvider` and `dmarcPolicy`. Turn off if you only care about hosting and platform. |
| `includeTls` | boolean | `true` | Leave on for certificate monitoring. Turn off if you are profiling domains that have no HTTPS endpoint and want to skip the handshake. |
| `includeRdap` | boolean | `true` | Turn off when your list is mostly ccTLDs without an RDAP service, or when you do not need registrar and expiry dates — it saves one request per domain. |
| `includeTechStack` | boolean | `true` | Leave on for lead scoring and competitor research. Turn off for a pure DNS/mail audit; it is the only section that fetches the homepage. |

Example input — this is the default, and it runs in about 10 seconds:

```json
{
    "startUrls": ["apify.com", "wordpress.org", "nextjs.org"],
    "maxItems": 20,
    "includeDns": true,
    "includeEmailSecurity": true,
    "includeTls": true,
    "includeRdap": true,
    "includeTechStack": true
}
```

### How much does it cost? 💰

Pay per event, so you pay for reports and not for run time. The prices below are the whole price list:

| Event | Charged when | Price |
|---|---|---|
| Actor start | Once per run, one event per GB of memory (one event at the default 1 GB) | $0.005 |
| Domain report | One domain analysed successfully (`status: ok`) | $0.05 |

- 100 domains ≈ **$2.01**, 1,000 domains ≈ **$20.01**, 10,000 domains ≈ **$200.01**.
- Rows with `status` `not_found` or `error` are **never charged**. A list full of dead domains costs you the start fee and nothing else.
- The Actor stops gracefully when your run's maximum charge is reached, and again when `maxItems` rows have been written.
- The free Apify plan includes enough credit to try the Actor on a few hundred domains.

### How it compares ⚖️

This Actor is built for **breadth across a list**, not depth on one host. Its strength is that a single $0.05 row answers questions that normally need five different lookup sites, and that the answer is structured, dated and diffable — which makes it good for scheduled monitoring and for feeding a scoring model.

It is not for you if you need a subdomain inventory, port scanning, vulnerability scanning or an audited technology census. Tech detection is fingerprint-based on the homepage only, so it is a strong signal rather than proof, and it reports `null` instead of guessing. If you need per-mailbox email verification, this is the wrong tool — see Limitations. And if you only ever check one domain at a time by hand, a free web lookup will do the job.

### Integrations, API and AI agents 🔌

Results export to **CSV, JSON, Excel**, XML and HTML from the **Output** tab, and can be pushed straight into **Google Sheets, Airtable, Slack or your CRM** through Apify integrations and webhooks (n8n, Make and Zapier included). Add a **Schedule** to re-run the same domain list daily and diff the results.

The Actor can also be called from the **Apify API** (Node.js, Python or curl — see the **API** tab), from the **Apify MCP server**, or by any AI agent: the input is a plain list of strings and every row is self-describing thanks to `status`, `error` and `warnings`, so an agent can tell "this domain does not exist" apart from "we could not check it" without parsing prose.

### FAQ ❓

**Do I need any API keys or accounts?** No. Every source is public and keyless: DNS-over-HTTPS, the registry's RDAP service, public ASN data, and plain HTTPS for the homepage and well-known files.

**Can it check DMARC and SPF for hundreds of domains at once?** Yes — that is the main use case. Each row gives you the SPF record and its `all` qualifier, the DMARC policy, subdomain policy, percentage and whether aggregate reporting is on, plus the DKIM selectors that answered. Sort by `dmarcPolicy` to find the spoofable domains first.

**Does it return WHOIS data?** It returns the modern equivalent: RDAP, the structured protocol that replaced WHOIS. You get registrar, creation/update/expiry dates, EPP status flags and nameservers. Registrant contact details are intentionally not returned, and for most gTLDs the registry redacts them anyway.

**Can it verify e-mail addresses?** No, and no HTTP-only Actor can. It tells you the mail provider and whether the domain publishes SPF, DMARC and DKIM, which is what you need to judge deliverability and spoofing risk. Mailbox-level verification needs SMTP on port 25, which Apify blocks.

**Why is `mailProvider` set to `other`?** The domain has MX records that do not match a known hosted provider — usually a self-hosted mail server or a niche host. Look at `dns.mx` for the actual exchanges.

**Why did some domains cost me nothing?** Because they came back `not_found` or `error`. Only `ok` rows are billed.

**How many domains can I run at once?** As many as you like; they are processed 10 at a time. Raise `maxItems` accordingly and use the run's maximum-charge setting as your budget cap.

**Can I run it on a schedule and get alerted on changes?** Yes. Schedule a daily run over your portfolio, then use a webhook or an integration to compare `tlsDaysToExpiry`, `dmarcPolicy` and `registrar` with yesterday's dataset.

**Can I get subdomains too?** Not yet. Certificate-transparency subdomain enumeration is slow and would need its own event; open an issue in the **Issues** tab if you need it.

### Limitations 🚧

Stated plainly, so nothing surprises you:

- **RDAP coverage is thin for some ccTLDs.** `.com`, `.net`, `.org` and most new gTLDs answer well. Several country registries (`.de`, `.jp`, `.ru`, `.eu` among them) publish no RDAP service at all, and others rate-limit it. Those rows come back with `rdap: null`, `registrar: null` and a `warnings` entry naming the reason — the rest of the report is unaffected.
- **No SMTP mailbox verification.** Apify's infrastructure blocks outbound port 25, and the large mail providers refuse RCPT probing from datacenter ranges anyway. This Actor reports the mail *provider* and the *domain's* e-mail posture (SPF/DMARC/DKIM); it does not and cannot tell you whether `jane@example.com` exists.
- **Tech detection is fingerprint-based and best-effort.** It matches response headers, cookies, `<meta name="generator">`, script/link URLs and HTML markers against a curated rule set, on the homepage only. It will miss a technology that loads deeper in the site or behind a tag manager, and it reports `null` rather than guessing. Treat it as a strong signal, not as an audited inventory.
- **One homepage, one moment in time.** Sub-pages, staging hosts and A/B variants are not visited, and a site behind a bot wall may answer the homepage request with a challenge page — then `techStack` reflects that page and a `warnings` entry says so.
- **DKIM is selector guessing.** Only the six most common selectors are probed, so an empty `dkimSelectorsFound` does not prove that DKIM is missing.
- **`hosting.asn` is the ASN of the first A record**, which for a proxied domain is the CDN's ASN, not the origin's.
- **Subdomain enumeration is not included**, and neither is HSTS-preload status or port scanning.

### Troubleshooting 🚦

| Symptom | Fix |
|---|---|
| Row has `status: not_found` | The name really does not resolve. Check for a typo, or that you passed the registrable domain rather than a subdomain that was never delegated. |
| Row has `status: error` with "Invalid domain name" | The input was not a host name (a phrase, an IP address, or `localhost`). |
| `rdap` is `null` with a warning | The TLD publishes no RDAP service, or the server rate-limited the run. Re-run later, or accept it for that ccTLD. |
| `techStack` is `null` with a warning | The homepage refused the request or timed out. Some sites block unknown user agents; DNS, mail, TLS and RDAP data are unaffected. |
| `cms` is `null` on a site you know uses X | The fingerprint is not in the rule set, or the marker is not on the homepage. Report it in the **Issues** tab and it will be added. |
| Fewer rows than domains you pasted | Duplicates are removed, and `maxItems` or your run's maximum charge stopped the run. Check the log for "Stopping gracefully". |

### Privacy 🔒

This Actor is built so that it cannot collect personal data:

- ✅ **RDAP contacts are never read.** Only the registrar entity is parsed. Registrant, administrative and technical contacts — names, e-mail addresses, phone numbers and postal addresses — are dropped even when a registry returns them unredacted. A golden test asserts this against a registry answer that contains all of them.
- ✅ **No mailbox verification, no e-mail addresses collected.** The Actor never probes SMTP and never harvests addresses from pages.
- ✅ **Public protocol data only** — DNS records, an RDAP registry answer, a TLS certificate, a homepage response and four well-known files, all published by the domain owner on purpose.
- ✅ **No login, no access controls circumvented, no captcha solving.** Every request is anonymous and read-only.
- ✅ **No page content is stored.** The homepage HTML is fingerprinted in memory; only the detected technology names and the `<title>` are saved.
- ⚠️ **Raw DNS records are returned verbatim** in `dns.txt` and in `emailSecurity.spf/dmarc.record`. If a domain owner published a personal address inside their own DMARC `rua=` tag, it is part of that public record. Set `includeDns` and `includeEmailSecurity` to `false` if you do not want raw records at all.

### Legal and data-protection notice ⚖️

This Actor reads only public protocol data that domain owners publish themselves: DNS records, RDAP registry answers, TLS certificates, homepage responses and well-known files. It does not log in, does not defeat access controls and does not extract private user data. Registrant, administrative and technical contacts are deliberately discarded before anything is written to the dataset.

Your results can still contain data that is personal in your context — a raw DNS TXT record chosen by the domain owner, for example. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not process personal data unless you have a legitimate reason to do so; if you are unsure whether your reason is legitimate, consult your lawyers. You are responsible for complying with applicable law and with the terms of the domains you analyse.

This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by any of the registries, registrars, hosting providers or technology vendors whose names may appear in its output. All trademarks belong to their respective owners.

# Actor input Schema

## `startUrls` (type: `array`):

One domain per line. Bare names (`stripe.com`), full URLs (`https://www.stripe.com/pricing`) and e-mail addresses (`sales@stripe.com`) are all accepted and reduced to the registrable host name; a leading `www.` is removed and duplicates are dropped. Every domain produces exactly one row.

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

Stop after this many rows have been saved. Each saved row with status `ok` is one billable result; `not_found` and `error` rows are free. Keep it small for test runs.

## `includeDns` (type: `boolean`):

Look up A, AAAA, MX, NS, TXT and CAA records over DNS-over-HTTPS and fill the `dns` block. Turn a section off to make the run faster; the other sections are unaffected.

## `includeEmailSecurity` (type: `boolean`):

Parse the SPF and DMARC records and probe six common DKIM selectors (google, selector1, selector2, k1, mail, default). Fills `emailSecurity`, `mailProvider` and `dmarcPolicy`.

## `includeTls` (type: `boolean`):

Open one TLS handshake to port 443 and report issuer, validity window, days to expiry, SAN count and whether the chain is trusted. Fills `tls` and `tlsDaysToExpiry`.

## `includeRdap` (type: `boolean`):

Ask the registry's RDAP service for registrar, creation/expiry/update dates, EPP status flags and nameservers. Registrant, admin and technical contacts are never read. Fills `rdap` and `registrar`.

## `includeTechStack` (type: `boolean`):

Fetch the homepage plus robots.txt, llms.txt, .well-known/security.txt and sitemap.xml, then fingerprint CMS, framework, analytics, chat and payment technologies. Fills `techStack`, `files`, `cms` and `cdn`.

## Actor input object example

```json
{
  "startUrls": [
    "apify.com",
    "wordpress.org",
    "nextjs.org"
  ],
  "maxItems": 20,
  "includeDns": true,
  "includeEmailSecurity": true,
  "includeTls": true,
  "includeRdap": true,
  "includeTechStack": 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 = {
    "startUrls": [
        "apify.com",
        "wordpress.org",
        "nextjs.org"
    ],
    "maxItems": 20,
    "includeDns": true,
    "includeEmailSecurity": true,
    "includeTls": true,
    "includeRdap": true,
    "includeTechStack": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("glasswing/domain-intelligence-report").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 = {
    "startUrls": [
        "apify.com",
        "wordpress.org",
        "nextjs.org",
    ],
    "maxItems": 20,
    "includeDns": True,
    "includeEmailSecurity": True,
    "includeTls": True,
    "includeRdap": True,
    "includeTechStack": True,
}

# Run the Actor and wait for it to finish
run = client.actor("glasswing/domain-intelligence-report").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 '{
  "startUrls": [
    "apify.com",
    "wordpress.org",
    "nextjs.org"
  ],
  "maxItems": 20,
  "includeDns": true,
  "includeEmailSecurity": true,
  "includeTls": true,
  "includeRdap": true,
  "includeTechStack": true
}' |
apify call glasswing/domain-intelligence-report --silent --output-dataset

```

## MCP server setup

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

```

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/pVxcFpVFEY9ydIhSC/builds/hF2tAJVem1yhWWyYU/openapi.json
