# Company Domain Profiler: Contacts, Tech Stack, Socials & DNS (`vicarious_film/domain-profiler`) Actor

Domain enrichment in one call: company data, contact details (emails, phones), social profiles, tech stack, email provider (MX), SPF/DMARC, domain age and SSL expiry for any company domain. Lead enrichment for Clay, n8n, Make and AI agents.

- **URL**: https://apify.com/vicarious\_film/domain-profiler.md
- **Developed by:** [Zac P](https://apify.com/vicarious_film) (community)
- **Categories:** Lead generation, AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 1,000 domain profileds

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

### What does Company Domain Profiler do?

**Company Domain Profiler turns a company domain into one clean JSON profile.** Send `stripe.com` and you get back one row: the **company name**, **contact emails and phones**, **social profiles**, **tech stack**, **email provider (MX)**, **SPF and DMARC**, **domain age (RDAP)**, **SSL certificate expiry**, and links to the **pricing, careers, docs, blog and status pages**.

One run replaces a contact-details scraper, a tech-stack lookup and a DNS/WHOIS tool, with no joins and no guesswork. It uses plain HTTP and public standards (DNS, RDAP, TLS, schema.org), so it's **fast and cheap**. It's built for **lead enrichment** in Clay, n8n and Make, and for **AI agents** researching a company over MCP.

#### 30-second example

Input:

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

Output (real run on Apify, trimmed):

```json
{
    "input": "stripe.com",
    "domain": "stripe.com",
    "status": "ok",
    "finalUrl": "https://stripe.com/",
    "company": {
        "name": "Stripe",
        "description": "Stripe powers online and in-person payment processing and financial solutions for businesses of all sizes.",
        "language": "en-us"
    },
    "contacts": {
        "emails": [
            {
                "value": "privacy@stripe.com",
                "type": "role",
                "sameDomain": true,
                "source": "https://stripe.com/privacy"
            },
            {
                "value": "careers@stripe.com",
                "type": "role",
                "sameDomain": true,
                "source": "https://stripe.com/careers"
            }
        ],
        "phones": [{ "value": "+18889262289", "source": "https://stripe.com/" }]
    },
    "socials": {
        "linkedin": "https://www.linkedin.com/company/stripe",
        "x": "https://x.com/stripe",
        "facebook": "https://www.facebook.com/StripeHQ",
        "instagram": "https://www.instagram.com/stripehq",
        "youtube": "https://www.youtube.com/@stripe",
        "tiktok": null,
        "github": "https://github.com/stripe"
    },
    "techStack": [
        { "name": "Nginx", "categories": ["Web servers", "Reverse proxies"], "confidence": 100, "source": "website" },
        { "name": "Salesforce", "categories": ["CRM"], "confidence": 100, "source": "dns" },
        { "name": "Linear", "categories": ["CRM"], "confidence": 100, "source": "dns" },
        { "name": "DigiCert", "categories": ["SSL/TLS certificate authorities"], "confidence": 100, "source": "tls" }
    ],
    "emailInfra": {
        "mxProvider": "Google Workspace",
        "acceptsMail": true,
        "spf": { "present": true, "allQualifier": "~all", "includes": ["spf1.stripe.com", "_spf.qualtrics.com"] },
        "dmarc": { "present": true, "policy": "reject", "pct": 100 }
    },
    "domainInfo": { "registeredAt": "1995-09-12T04:00:00.000Z", "ageDays": 11336, "registrar": "SafeNames Ltd." },
    "ssl": { "issuer": "DigiCert Inc", "validTo": "2026-11-12T23:59:59.000Z", "daysUntilExpiry": 48, "trusted": true },
    "keyPages": {
        "pricing": "https://stripe.com/pricing",
        "careers": "https://stripe.com/careers",
        "docs": "https://docs.stripe.com/",
        "status": "https://status.stripe.com/",
        "changelog": "https://docs.stripe.com/changelog",
        "llmsTxt": "https://stripe.com/llms.txt"
    },
    "pagesFetched": 6,
    "durationMs": 2312,
    "errors": []
}
```

![Overview of profiled domains: company, email, LinkedIn, email provider, SPF, DMARC, SSL expiry](https://api.apify.com/v2/key-value-stores/BqQeE21yBOZtJB9ll/records/screenshot-overview.png)

![Tech stack per domain, including SaaS tools found in DNS verification records](https://api.apify.com/v2/key-value-stores/BqQeE21yBOZtJB9ll/records/screenshot-techstack.png)

### Why use Company Domain Profiler?

- **One row per domain, always.** Every input domain returns exactly one item, in the order you sent them, with your original `input` echoed back for joins. Dead or parked domains come back too, as `status: "unreachable"`.
- **Predictable price.** You pay **per domain profiled**, not per page crawled, so a site with 3,000 pages costs the same as one with 3. **Unreachable and parked domains are free.**
- **More than contacts.** Email security posture (SPF/DMARC), email provider, domain age and SSL expiry are what agencies and sales teams use to qualify and personalise outreach ("your DMARC policy is `none`").
- **Fast and reliable.** HTTP only, no headless browser. On a 500-domain benchmark: **3.7 minutes, 0 crashes, a result for every live domain**, a median of ~2.5 s per domain. A failure in one part (say, RDAP) never loses the rest of the profile: it's listed in `errors[]` and the item still returns.
- **Agent-ready.** Small, stable, documented JSON that works well as an MCP tool for Claude, ChatGPT and other agents.

#### Use cases

- **Lead enrichment:** add company name, generic contact emails, LinkedIn and email provider to a list of domains in Clay, HubSpot or a spreadsheet.
- **Prospect audits for agencies:** find sites with no DMARC, an expiring SSL certificate, or an outdated CMS.
- **Market mapping:** see which tools a segment uses (Shopify vs WooCommerce, HubSpot vs Salesforce, Google Workspace vs Microsoft 365).
- **AI research agents:** give an agent one tool call that returns a whole company profile.

### How to use Company Domain Profiler

1. Open the Actor and go to the **Input** tab.
2. Paste your domains into **Company domains**, one per line. URLs are fine; they're normalised to the domain.
3. Optionally pick **Modules** to collect only what you need (all are on by default).
4. Click **Start**. A few hundred domains usually take a few minutes.
5. Download the results as JSON, CSV or Excel, or use the API and integrations below.

Want to see it first? Run a ready-made example: [Profile 10 SaaS companies](https://apify.com/vicarious_film/domain-profiler/examples/profile-10-saas-companies) or the [Email security audit](https://apify.com/vicarious_film/domain-profiler/examples/email-security-audit) (MX, SPF, DMARC and SSL for 10 domains).

### Input

| Field                   | Type             | Default    | Description                                                                                               |
| ----------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------- |
| `domains`               | array of strings | (required) | Company domains or URLs. Deduplicated; output keeps input order.                                          |
| `modules`               | array            | all        | Any of `identity`, `contacts`, `socials`, `techStack`, `emailInfra`, `domainInfo`, `ssl`, `keyPages`.     |
| `maxPagesPerDomain`     | integer 0–20     | 5          | Extra pages read besides the homepage (contact, about, impressum, careers…). 5 are included in the price. |
| `excludePersonalEmails` | boolean          | false      | Return only role addresses (info@, sales@…), dropping ones that look like a person's.                     |
| `maxConcurrency`        | integer          | 25         | Domains processed in parallel.                                                                            |

### Output fields

You can download the dataset in formats such as JSON, CSV, Excel or HTML. Each item has these fields:

| Field                                                                                                                   | What it contains                                                                                                                         | Source                                                                                  |
| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `status`                                                                                                                | `ok`, `partial` (site blocked or a module failed; DNS/SSL/RDAP still filled) or `unreachable` (no website or parked; not charged)        | —                                                                                       |
| `company`                                                                                                               | `name`, `description`, `logo`, `favicon`, `address`, `foundingDate`, `language`                                                          | schema.org JSON-LD, Open Graph, `<title>`                                               |
| `contacts.emails[]`                                                                                                     | `value`, `type` (`role`/`personal`), `sameDomain`, `source` page                                                                         | `mailto:` links, page text (including `[at]` and Cloudflare-obfuscated emails), JSON-LD |
| `contacts.phones[]`                                                                                                     | E.164 number + `source` page                                                                                                             | `tel:` links, JSON-LD, formatted numbers in text (validated)                            |
| `socials`                                                                                                               | `linkedin`, `x`, `facebook`, `instagram`, `youtube`, `tiktok` and `github` **company** pages (share links and personal profiles dropped) | Links and JSON-LD `sameAs`                                                              |
| `techStack[]`                                                                                                           | `name`, `categories`, `confidence`, `version`, `source` (`website`, `dns` or `tls`)                                                      | 7,600+ open-source fingerprints over headers, HTML, scripts, meta, cookies and DNS      |
| `emailInfra`                                                                                                            | `mxRecords`, `mxProvider`, `acceptsMail`, `spf` (record, includes, `~all`/`-all`), `dmarc` (policy, subdomain policy, pct)               | DNS                                                                                     |
| `domainInfo`                                                                                                            | `registeredAt`, `expiresAt`, `updatedAt`, `ageDays`, `registrar`, `rdapServer`                                                           | RDAP (null for TLDs without RDAP, e.g. .de)                                             |
| `ssl`                                                                                                                   | `host`, `issuer`, `validFrom`, `validTo`, `daysUntilExpiry`, `subjectAltNames`, `trusted`                                                | TLS handshake                                                                           |
| `keyPages`                                                                                                              | `pricing`, `careers`, `docs`, `blog`, `status`, `changelog`, `contact`, `about`, `llmsTxt`, `robotsTxt`, `sitemapXml`                    | Site links, ATS hosts (Lever, Greenhouse…), well-known files                            |
| `errors[]`                                                                                                              | `module`, `code`, `message` for anything that failed                                                                                     | —                                                                                       |
| `input`, `domain`, `finalUrl`, `httpStatus`, `redirectedToDomain`, `parked`, `pagesFetched`, `durationMs`, `profiledAt` | Run metadata                                                                                                                             | —                                                                                       |

> **Pairs well with [Company Hiring Signals](https://apify.com/vicarious_film/company-hiring-signals):** the same accounts' open roles, hiring velocity and `hiring_sales` / `hiring_engineering` signals, $0.006 a company.

### How much does it cost to profile company domains?

Pricing is pay-per-event:

| Event                                                                  | Free plan  | Starter | Scale   | Business |
| ---------------------------------------------------------------------- | ---------- | ------- | ------- | -------- |
| Domain profiled (`ok` or `partial`)                                    | **$0.006** | $0.0055 | $0.005  | $0.0045  |
| Unreachable or parked domain                                           | **free**   | free    | free    | free     |
| Extra page beyond 5 per domain (only if you raise `maxPagesPerDomain`) | $0.001     | $0.0009 | $0.0008 | $0.0007  |

**1,000 domains ≈ $6** on the free plan ($4.50 on Business). A list of 10,000 domains with ~15% dead or parked costs about $51. Apify's free plan credit covers roughly the first 800 domains each month.

### Integrations

#### API (one call, results back)

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

#### n8n

Use the **Apify** node (Run an Actor and get dataset → Actor `vicarious_film/domain-profiler`, input `{"domains": ["{{ $json.domain }}"]}`), or an **HTTP Request** node that POSTs to the `run-sync-get-dataset-items` URL above. Each output item maps 1:1 to an input domain.

#### Make

Add **Apify → Run an Actor** (Actor: `vicarious_film/domain-profiler`, "Run synchronously" on), then **Apify → Get Dataset Items** with the run's `defaultDatasetId`.

#### Clay

Add an **HTTP API** enrichment column: method `POST`, URL `https://api.apify.com/v2/acts/vicarious_film~domain-profiler/run-sync-get-dataset-items?token=YOUR_TOKEN`, body `{"domains": ["{{Domain}}"]}`. Then map `company.name`, `contacts.emails`, `socials.linkedin` and `emailInfra.mxProvider` into columns.

#### AI agents (MCP)

Add Apify's MCP server with this Actor as a tool, e.g. for Claude:

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com/?actors=vicarious_film/domain-profiler" } } }
```

Then ask: *"Profile stripe.com and tell me their email provider and DMARC policy."*

### Tips

- **Only need email security?** Set `modules` to `["emailInfra", "ssl", "domainInfo"]`. No website pages are fetched, so it's very fast. The [Email security audit](https://apify.com/vicarious_film/domain-profiler/examples/email-security-audit) task is set up this way.
- **Homepage only:** set `maxPagesPerDomain` to 0. You'll get fewer contacts but faster runs.
- **Joining back to your list:** use `input` (your original value) or `domain` (normalised).
- **Want to know who's hiring?** `keyPages.careers` tells you a careers page exists; [Company Hiring Signals](https://apify.com/vicarious_film/company-hiring-signals) turns the same domains into open roles, new roles in 30 days and ready-to-filter signals like `hiring_sales`.

### FAQ

**Why are some emails missing?** The Actor only returns contacts the company publishes in its site's HTML (homepage plus up to 5 pages such as contact, about and impressum). Sites that load contacts with JavaScript, or behind forms, return fewer contacts; their `errors[]` then includes `JS_RENDERED`. You're charged the same flat price either way, never per page.

**What does `partial` mean?** The website blocked automated access (e.g. a Cloudflare challenge) or one module failed. DNS, SSL and RDAP data are still returned. `errors[]` says what happened.

**Will I be charged for dead domains?** No. `unreachable` (no website, DNS failure, parked or for-sale) is never charged.

**Is the tech stack accurate?** Detection uses 7,600+ community-maintained fingerprints from open-source Wappalyzer data, matched against headers, HTML, scripts, cookies, meta tags and DNS records. `source: "dns"` entries come from DNS verification records (e.g. a company that verified its domain with a SaaS tool), which shows tools they use even when those tools aren't on the website.

**Do you scrape LinkedIn or other platforms?** No. We only link to the company pages the site itself links to. Everything comes from the company's own website and public internet infrastructure (DNS, RDAP, TLS).

**Results come back in a different order?** They don't: output order always matches input order, and duplicates are removed.

### GDPR and legal

This Actor collects only information a company publishes about itself on its own website, plus public DNS, RDAP and TLS data. Some returned emails may belong to individuals (`type: "personal"`); under GDPR and similar laws, **you** are responsible for having a lawful basis to process them. Use **`excludePersonalEmails: true`** to keep only role addresses (info@, sales@…). RDAP registrant personal data is never collected. Don't use the results for unsolicited bulk email where that's illegal. See [Apify's guide to ethical scraping](https://blog.apify.com/is-web-scraping-legal/).

### Support

Found a wrong or missing field? Open an issue on the **Issues** tab with the domain and what you expected. Issues get a reply within 24 hours. Need a custom field or integration? Ask in an issue.

# Actor input Schema

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

Company domains to profile, one per line. Full URLs are fine too: "https://www.stripe.com/about" becomes "stripe.com". Duplicates are removed, and results come back in the same order you gave them.

## `modules` (type: `array`):

Parts of the profile to collect; empty = all. identity, contacts, socials, techStack, emailInfra (MX/SPF/DMARC), domainInfo (RDAP), ssl, keyPages.

## `maxPagesPerDomain` (type: `integer`):

How many pages to read besides the homepage, e.g. contact, about, impressum and careers. 5 pages are included in the per-domain price; each page beyond 5 is charged as a deep-crawl page. Set 0 to read the homepage only.

## `excludePersonalEmails` (type: `boolean`):

Return only role addresses (info@, sales@, support@…). Addresses that look like a person's own (jane.doe@) are dropped. Use this to reduce GDPR exposure.

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

How many domains to profile in parallel. The default is fine for most runs.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "allbirds.com"
  ],
  "modules": [],
  "maxPagesPerDomain": 5,
  "excludePersonalEmails": false,
  "maxConcurrency": 25
}
```

# 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": [
        "stripe.com",
        "allbirds.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vicarious_film/domain-profiler").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",
        "allbirds.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("vicarious_film/domain-profiler").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",
    "allbirds.com"
  ]
}' |
apify call vicarious_film/domain-profiler --silent --output-dataset

```

## MCP server setup

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

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/TVXEqduw8dwKoyeOe/builds/j54UQfSML8IzkOGuS/openapi.json
