# Domain Lookup: Tech Stack, Email Provider, DNS, WHOIS & SSL (`locaihost/domain-intel`) Actor

One row per domain: email provider (Google Workspace, Microsoft 365…), SPF/DMARC/BIMI, DNS provider, hosting & CDN, CMS and tech stack (Shopify, WordPress, HubSpot…), SSL expiry and registrar dates. Paste up to 5,000 domains or URLs. Public DNS, RDAP and TLS data only, no personal data.

- **URL**: https://apify.com/locaihost/domain-intel.md
- **Developed by:** [locaihost data](https://apify.com/locaihost) (community)
- **Categories:** Lead generation, Developer tools, MCP servers
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.20 / 1,000 domain analyseds

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

## Domain Lookup: Tech Stack, Email Provider, DNS, WHOIS & SSL

Paste a list of company domains and get **one row per domain** with everything a sales, marketing or security team wants to know about it:

- **Email:** who hosts it (Google Workspace, Microsoft 365, Zoho, Proton…), any security gateway in front of it (Proofpoint, Mimecast…), and whether **SPF, DMARC, BIMI and MTA-STS** are set up.
- **Website:** HTTP status, redirects, HTTPS and HSTS, response time, page title, and the **hosting and CDN** behind it (Shopify, Vercel, Netlify, WP Engine, Cloudflare, CloudFront, Akamai, Fastly…).
- **Tech stack:** **CMS** (WordPress, Shopify, Wix, Squarespace, Webflow, Ghost, Drupal, HubSpot CMS…), e-commerce, analytics (GA4, GTM, Segment, Hotjar, Plausible…), JavaScript frameworks (Next.js, Nuxt, React, Vue, Angular…), chat widgets (Intercom, Drift, Zendesk, Crisp…), payments (Stripe, PayPal, Klarna…) and marketing tools (HubSpot, Marketo, Klaviyo, Meta Pixel…).
- **SSL:** certificate issuer, validity dates and **days until expiry**.
- **Registration:** registrar, creation date, **expiry date** and status, straight from the official registry (RDAP).

Everything comes back in one run, as one flat row per domain, ready for a spreadsheet or CRM.

![Real rows from this Actor's output: email provider, DNS provider, CDN, DMARC policy, detected tech, SSL days left and registrar for five company domains](https://locaihost.org/img/readme/domain-intel.png)

### Who is it for?

- **Lead generation & sales ops.** Segment accounts by technology: every Shopify store in your list, every company on Microsoft 365, everyone still on Universal Analytics. Technographic filters make outbound far more relevant.
- **Agencies & freelancers.** Find prospects running WordPress without HSTS, sites with SSL expiring this month, or companies with no DMARC policy. Each one is a concrete reason to get in touch.
- **Security & IT audits.** Check email authentication (SPF `-all` vs `~all`, DMARC `p=none` vs `reject`), certificate expiry and domain expiry across a whole portfolio of brands or suppliers in minutes.
- **Market research.** Measure hosting, CMS and email-provider market share across any list of companies.

### Why this one

- **One row per domain, everything in one run.** No juggling separate DNS, WHOIS, SSL and tech-lookup tools and joining the results.
- **Accuracy over a long list.** Technology detection uses product-specific signals only (the vendor's own script host, a global it defines, a header it sets). A blog that *writes about* Shopify won't be tagged as a Shopify store.
- **Honest results.** If a lookup fails, the field is `null` and `errors` says why. You never get a silent "no DMARC" just because a DNS server timed out.
- **Sees through email gateways.** If MX points at Proofpoint or Mimecast, SPF tells us whether the mailboxes are on Microsoft 365 or Google Workspace.
- **Paste anything.** `stripe.com`, `https://www.shopify.com/pricing`, `info@acme.com` and `www.bbc.co.uk` all work. Duplicates are merged, and junk lines are skipped and listed in the run summary.
- **No personal data.** No registrant names, emails or phone numbers. The local part of DMARC report addresses is redacted (`mailto:***@vendor.com`).

### How to use

1. Click **Try for free** and paste your domains (one per line, up to 5,000).
2. Optionally turn off sections you don't need. *DNS only* runs are the fastest.
3. Run it and open the **Overview**, **Email security** or **Tech stack** table, or export everything as CSV, Excel or JSON.
4. Re-check a portfolio regularly by saving the input as a **task** and adding a **schedule**, for example a monthly SSL and domain-expiry check.

#### Example input

```json
{
  "domains": ["stripe.com", "https://www.allbirds.com/", "hubspot.com", "bbc.co.uk", "info@nextjs.org"],
  "checkWebsite": true,
  "checkSsl": true,
  "checkRegistration": true
}
```

#### Example output (one row)

```json
{
  "domain": "allbirds.com",
  "registered": true,
  "resolves": true,
  "ipv4": ["23.227.38.32"],
  "nameservers": ["ns2.markmonitor.com", "ns3.markmonitor.com", "…"],
  "dnsProvider": "MarkMonitor",
  "mxRecords": ["allbirds-com.mail.protection.outlook.com"],
  "emailProvider": "Microsoft 365",
  "emailSecurityGateway": null,
  "hasSpf": true,
  "spfRecord": "v=spf1 include:allbirds_com._es.easydmarc.com include:spf.protection.outlook.com ~all",
  "spfAll": "~all",
  "hasDmarc": true,
  "dmarcRecord": "v=DMARC1;p=quarantine;pct=100;rua=mailto:***@allbirds.com;ruf=mailto:***@allbirds.com;fo=1",
  "dmarcPolicy": "quarantine",
  "dmarcPct": 100,
  "hasBimi": true,
  "hasMtaSts": false,
  "caaIssuers": [],
  "httpStatus": 200,
  "finalUrl": "https://www.allbirds.com/",
  "redirectCount": 1,
  "https": true,
  "hsts": true,
  "server": "cloudflare",
  "title": "Allbirds: Comfortable, Sustainable Shoes & Apparel",
  "responseTimeMs": 230,
  "cdn": "Cloudflare",
  "hosting": "Shopify",
  "cms": null,
  "ecommerce": ["Shopify"],
  "analytics": ["Google Tag Manager", "Microsoft Clarity"],
  "frameworks": [],
  "chatWidgets": [],
  "payments": [],
  "marketing": [],
  "technologies": ["Shopify", "Google Tag Manager", "Microsoft Clarity"],
  "sslIssuer": "Let's Encrypt",
  "sslValidTo": "2027-01-07T03:10:03.000Z",
  "sslDaysToExpiry": 88,
  "sslValid": true,
  "tlsVersion": "TLSv1.3",
  "registrar": "MarkMonitor Inc.",
  "registeredAt": "2002-01-09T15:24:37Z",
  "expiresAt": "2028-01-09T15:24:37Z",
  "daysToDomainExpiry": 456,
  "domainStatus": ["client delete prohibited", "client transfer prohibited", "…"],
  "dnssec": false,
  "errors": {},
  "checkedAt": "2026-10-10T09:00:00.000Z"
}
```

#### Fields

| Field | Meaning |
|---|---|
| `domain` / `input` | The registrable domain we checked, and what you typed |
| `registered` / `resolves` | Found in the registry / has an IP address. A typo'd or dead domain shows `false` |
| `dnsProvider` | Inferred from name servers: Cloudflare, AWS Route 53, Azure DNS, Google Cloud DNS, GoDaddy, Namecheap, NS1, Akamai… or `Self-hosted` |
| `emailProvider` | Where the mailboxes live, from MX (and SPF when a gateway hides it) |
| `emailSecurityGateway` | Inbound filter in front of the mailboxes: Proofpoint, Mimecast, Cisco, Barracuda, Broadcom… |
| `spfAll` | How strict SPF is: `-all` (fail), `~all` (softfail), `?all`, `+all` |
| `dmarcPolicy` | `none`, `quarantine` or `reject`. `hasDmarc: false` = no DMARC record at all |
| `dmarcReportDomains` | Where aggregate reports go, which often reveals the DMARC vendor |
| `hasBimi` / `hasMtaSts` / `caaIssuers` | Brand logo in inboxes / enforced TLS for inbound mail / which CAs may issue certificates |
| `httpStatus`, `finalUrl`, `redirectCount` | Result of one GET of the homepage, following redirects |
| `https` / `hsts` | Final page served over HTTPS / with Strict-Transport-Security |
| `responseTimeMs` | Time to the final response's headers, including redirects |
| `cdn` / `hosting` | Edge network and hosting platform, from response headers, CNAME, reverse DNS and published IP ranges |
| `cms`, `ecommerce`, `analytics`, `frameworks`, `chatWidgets`, `payments`, `marketing` | Technologies grouped for filtering. `technologies` lists them all |
| `sslDaysToExpiry` | Days until the certificate on port 443 expires (negative = already expired). `sslValid` = trusted chain and matches the name |
| `registrar`, `registeredAt`, `expiresAt`, `daysToDomainExpiry`, `domainStatus`, `dnssec` | From the registry's RDAP service |
| `errors` | Per-section explanation when something couldn't be checked: `dns`, `website`, `tech`, `ssl`, `registration` |

### Pricing

**Pay per domain.** You're charged once for each domain row returned, however much was found for it. Invalid lines and duplicates you pasted are skipped for free.

| Apify plan | Price per 1,000 domains |
|---|---|
| Free and Starter | **$4.00** |
| Scale | $3.00 |
| Business and above | $2.20 |

**Worked examples.** Enriching a 1,000-account list costs **$4.00** on Starter. A monthly SSL and domain-expiry check on a 50-domain brand portfolio costs 50 × $0.004 = **$0.20 a month**. Apify's $0.00005 run-start fee comes on top.

**Free Apify plan:** each run returns up to **100 domains**. Any paid Apify plan removes the cap.

### Use with AI agents (MCP)

Use this Actor as a tool in Claude, Cursor, VS Code or any MCP client through the [Apify MCP server](https://mcp.apify.com). Agents find it by searching for "email provider domain", "DNS" or "tech stack".

1. Add the server. In Claude Code: `claude mcp add apify https://mcp.apify.com/ -t http`. In Cursor or Claude Desktop, add `https://mcp.apify.com` as a remote MCP server. Sign in with your Apify account when asked, or send your API token as `Authorization: Bearer <APIFY_TOKEN>`.
2. To expose only this Actor as a tool, use `https://mcp.apify.com/?tools=locaihost/domain-intel`.
3. Ask in plain language, for example:

> Which of these companies use Google Workspace or Microsoft 365, and which have no DMARC policy? stripe.com, shopify.com, grab.com, monzo.com

The minimal input an agent should send:

```json
{
  "domains": ["stripe.com", "shopify.com", "grab.com", "monzo.com"]
}
```

For an email-only question, add `"checkWebsite": false, "checkSsl": false, "checkRegistration": false`. The run is faster and the price per domain is the same.

### Integrations

Call it from any language with the Apify API. This request waits for the run and returns one JSON row per domain (synchronous runs time out after 5 minutes, about 150–300 domains; use the async run endpoint for longer lists):

```bash
curl -X POST "https://api.apify.com/v2/acts/locaihost~domain-intel/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["stripe.com", "shopify.com", "bbc.co.uk"]}'
```

Python, with `pip install apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("locaihost/domain-intel").call(run_input={
    "domains": ["stripe.com", "shopify.com", "grab.com"],
})
for d in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(d["domain"], d["emailProvider"], d["dmarcPolicy"], d["cms"], d["sslDaysToExpiry"])
```

**Schedules:** save a portfolio as a task and add a monthly schedule in Apify Console → Schedules to watch SSL and domain expiry.

**Where the results go:** Apify's built-in integrations send each run's dataset to Slack, email, Google Sheets, Zapier, Make, n8n (the Apify node) or any webhook, or straight into your CRM through Zapier or Make.

### FAQ

**How fresh is the data?**
Live. DNS, the homepage, the certificate and the registry record are all read at run time. Nothing comes from a cached database.

**Is this legal? Where does the data come from?**
Public DNS, one homepage request per domain (robots.txt respected), the public TLS certificate, and each registry's official RDAP service. These are the same lookups any mail server or browser makes.

**Does it return personal data?**
No. Registrant names, emails and phone numbers are never collected. The local part of DMARC report addresses is redacted (`mailto:***@vendor.com`). One caveat: the `input` column echoes exactly what you pasted, so if you paste a person's email address to look up its domain, that address is in your output. Paste domains where you can.

**Why is the tech stack empty for a site?**
Detection reads the homepage HTML and headers. Tools loaded later by JavaScript can't be seen, and some sites answer bots with `403`. `errors.tech` says which case applies.

**How many domains per run, and how fast?**
Up to 5,000 domains per run, at roughly 30–60 domains per minute. A DNS-only run is much faster.

**Why is registration data missing for `.io` or `.de`?**
Those registries don't offer RDAP. The row says so in `errors.registration`; everything else still works.

### Troubleshooting

| What you see | What it means and what to do |
|---|---|
| `registered: false`, `resolves: false` and `errors.dns: "Domain does not exist in DNS (NXDOMAIN)"` | A typo or a dead domain. The row is still returned so your list stays complete. |
| `Done: 98 domains analysed, 2 invalid inputs skipped …` | Two lines weren't public domains (IPs, internal names, bare suffixes like `co.uk`, or junk). They're listed in **SUMMARY** and free. |
| `No valid public domains in the input …` | Nothing in the list was a domain. The run ends as succeeded and you are not charged. |
| `Only the first 5000 domains are processed per run` | Split bigger lists across several runs. |
| `(free-plan cap of 100 …)` | The free plan's per-run cap. Use any paid Apify plan for longer lists. |
| `errors.website` says robots.txt disallows the homepage | The site asks crawlers like ours not to fetch it. We skip the website and tech checks; DNS, email, SSL and registration still come back. |
| Run failed: `No results: all N domains failed — see the SUMMARY record.` | Every lookup errored, usually a network problem on the run. Try again. |

### Good to know

- **Polite by design.** Each website gets one `robots.txt` request, one homepage GET and one TLS handshake, one after another, never in parallel. About 10 domains are checked at a time. If `robots.txt` disallows the homepage for our crawler (`locaihost-domain-intel`), we skip the website and tech sections and say so in `errors.website`.
- **Tech detection reads the homepage HTML and headers.** Tools loaded only later by JavaScript (some chat widgets, tag-manager-injected pixels) can't be seen, so an empty list means "nothing visible on the homepage", not "definitely not used".
- **Bot walls.** Some sites (often government or banking) answer automated requests with `403`. You still get DNS, email, SSL, registration and usually CDN/hosting. `errors.tech` explains the gap.
- **Hosting behind a CDN.** When a site sits behind Cloudflare or another proxy and the origin sends no identifying headers, `hosting` is `null`. We don't guess.
- **Registration data.** Comes from each registry's RDAP service. A few country domains (e.g. `.io`, `.de`, `.co`, `.so`, `.me`) don't offer RDAP, so those rows explain that in `errors.registration`. Registrant and contact details are never collected.
- **Subdomains** are checked at the registrable domain (`shop.acme.co.uk` → `acme.co.uk`). Customer sites on platforms such as `*.myshopify.com` or `*.github.io` are kept as they are.
- **Speed.** Roughly 30–60 domains per minute, so 1,000 domains take about 15–30 minutes. A DNS-only run (website, SSL and registration off) is much faster.
- **Run summary.** The `SUMMARY` record has counts by email provider, site platform and hosting, how many domains lack DMARC or have SSL expiring within 30 days, and any inputs that weren't domains.

### Feedback

Missing a technology, provider or field you need? Open an issue on the **Issues** tab. Replies usually within a day.

# Actor input Schema

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

One per line, up to 5,000. Bare domains (stripe.com), URLs (https://www.shopify.com/pricing) or email addresses (info@acme.com) all work: each is reduced to its registrable domain and duplicates are merged.

## `checkWebsite` (type: `boolean`):

Fetch each homepage once (robots.txt respected) for HTTP status, redirects, HTTPS/HSTS, hosting/CDN and the tech stack. Turn off for a faster DNS-only run.

## `checkSsl` (type: `boolean`):

Read the certificate on port 443: issuer, validity dates and days until expiry.

## `checkRegistration` (type: `boolean`):

Registrar, creation and expiry dates and status from the official registry (RDAP). Contact details are never collected. Some country domains (.io, .de, .co…) have no RDAP service.

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

Stop after this many domains. 0 = no limit.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "shopify.com",
    "hubspot.com",
    "bbc.co.uk",
    "nextjs.org"
  ],
  "checkWebsite": true,
  "checkSsl": true,
  "checkRegistration": true,
  "maxResults": 0
}
```

# Actor output Schema

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

One record per domain (overview view).

## `emailSecurity` (type: `string`):

Email provider, SPF, DMARC, BIMI and MTA-STS per domain.

## `techStack` (type: `string`):

CMS, e-commerce, analytics, frameworks, chat, payments and marketing tools per domain.

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

Counts by email provider, CMS and hosting, invalid inputs and failures.

# 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": [
        "stripe.com",
        "shopify.com",
        "hubspot.com",
        "bbc.co.uk",
        "nextjs.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("locaihost/domain-intel").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": [
        "stripe.com",
        "shopify.com",
        "hubspot.com",
        "bbc.co.uk",
        "nextjs.org",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("locaihost/domain-intel").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": [
    "stripe.com",
    "shopify.com",
    "hubspot.com",
    "bbc.co.uk",
    "nextjs.org"
  ]
}' |
apify call locaihost/domain-intel --silent --output-dataset

```

## MCP server setup

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

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/XRAfTUezTqloGBQwp/builds/Egsj2yl2ryap4VWTe/openapi.json
