# Website Tech Stack & Lead Enrichment Scraper (`eiv/website-intelligence-scraper`) Actor

Profile any domain in ~250ms: 300+ technologies, ad pixel IDs (Meta, Google, TikTok, LinkedIn), emails, phones, socials, SPF/DKIM/DMARC and hosting. No browser, so it runs thousands of domains per run. Built for lead gen, sales intelligence and competitor research. From $2.50 / 1,000 domains.

- **URL**: https://apify.com/eiv/website-intelligence-scraper.md
- **Developed by:** [Eimantas V](https://apify.com/eiv) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 domain profiles

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/platform/actors/running/actors-in-store#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

## Website Tech Stack & Lead Enrichment Scraper

Turn a list of domains into rich, structured company profiles — **technology stack, advertising pixel IDs, contact details, e-mail infrastructure, hosting and firmographic signals**.

No browser, no JavaScript rendering — just fast HTTP and DNS. That is why this Actor costs a fraction of a browser-based crawler and can process tens of thousands of domains in a single run.

```
Input:   gymshark.com
Output:  Shopify · Klaviyo · Attentive · Judge.me · Recharge · Meta Pixel 12345…
         Google Workspace · DMARC p=reject · Cloudflare · hello@… · 6 socials
         martechMaturityScore 78 · runsPaidAds true · isHiring true
```

***

### What you get for every domain

| Group | Fields |
|---|---|
| **Technology stack** | 300+ fingerprints across 45 categories — e-commerce platform, CMS, page builder, JS framework, CDN, hosting, analytics, advertising, CRM, e-mail marketing, SMS, live chat, reviews, payments, BNPL, subscriptions, search, personalisation, loyalty, cookie consent, bot protection, error tracking, auth. Each detection carries a **version**, a **confidence score** and the **evidence that matched**, so you can audit anything you don't trust. |
| **Ad pixel & tracking IDs** | Actual account IDs — Meta Pixel, Google Ads, GA4, Universal Analytics, GTM, TikTok, LinkedIn, Pinterest, Snap, Reddit, X, Microsoft UET, Hotjar, Klaviyo, HubSpot, Intercom, Segment, Amplitude, Mixpanel — plus `adPixelCount`. |
| **Contacts** | E-mail addresses split into **role** and **personal**, phone numbers, postal addresses, country code, and the contact/about page URLs. |
| **Social profiles** | Canonical LinkedIn, Facebook, Instagram, X, YouTube, TikTok, Pinterest, GitHub, Reddit and Threads URLs. Share buttons and individual posts are filtered out. |
| **E-mail infrastructure** | Mailbox provider, MX records, SPF policy, **every third-party sender authorised in SPF**, DMARC policy and reporting addresses, resolving DKIM selectors, BIMI, an `isSpoofable` flag and a 0–100 security score. |
| **Commerce** | Platform, Shopify theme / plan tier / store ID, currency, payment providers, BNPL, cart & checkout presence, and installed store-front apps. |
| **DNS & hosting** | A/AAAA, nameservers, DNS operator, SaaS domain-verification tokens, CDN, hosting provider, server software, edge PoP. |
| **TLS** *(optional)* | Issuer, validity, days to expiry, and the **Subject Alternative Names that expose a company's other brands and staging hosts**. |
| **Security headers** | HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, `security.txt`, 0–100 score. |
| **Pages & robots** | Sitemaps, **which AI crawlers are blocked**, and the pricing / careers / blog / login / docs / app-subdomain URLs. |
| **Business signals** | B2B-SaaS or agency classification, free trial, demo CTA, hiring, newsletter, live chat, paid advertising, **server-side tagging**, cookie consent, a 0–100 **martech maturity score**, and plain-English highlights. |

***

### Who this is for

- **Outbound sales & lead gen** — build "runs Shopify Plus + Klaviyo but not Attentive" target lists, then enrich them with e-mails and LinkedIn URLs in the same pass.
- **Agencies** — qualify prospects before the call. You know their platform, ad channels, ESP and martech maturity before you dial.
- **Competitive intelligence** — track which vendors your market is adopting, at scale, on a schedule.
- **Data teams** — a stable, documented, fixed-column schema that loads straight into BigQuery, Snowflake or pandas. Every field is always present (null when not collected), so your table never changes shape between runs.
- **E-mail deliverability & security vendors** — the `isSpoofable` flag plus DMARC policy is a ready-made prospect list.

***

### Input

Paste domains, one per line. Bare domains, full URLs and even e-mail addresses all work.

```json
{
  "domains": ["gymshark.com", "https://www.stripe.com/pricing", "jane@notion.so"],
  "enrichContacts": true,
  "enrichEmailInfrastructure": true,
  "expandTagManager": true,
  "maxConcurrency": 40
}
```

You can also pass `startUrls` (to chain this Actor onto another one) or `sourceDatasetId` (to enrich a dataset produced by an earlier run — no CSV round-trip).

**Duplicates and `www`/apex variants of the same site are merged automatically**, so a messy list never bills you twice for one company.

#### Options

| Option | Default | Notes |
|---|---|---|
| `enrichContacts` | `true` | Fetches contact/about/imprint pages. Roughly doubles the number of populated fields. |
| `enrichEmailInfrastructure` | `true` | Pure DNS — fast and very cheap. |
| `expandTagManager` | `true` | **Strongly recommended.** See below. |
| `enrichTls` | `false` | Adds a TLS handshake and one request per domain. |
| `maxExtraPages` | `2` | Contact/about/imprint pages fetched per domain. |
| `maxConcurrency` | `40` | About 20 per GB of assigned memory is a good rule of thumb. |
| `maxHtmlKb` | `1000` | See the trade-off below. |
| `flattenOutput` | `false` | Turn on for CSV/spreadsheets; leave off for warehouses. |
| `skipFailedDomains` | `false` | Off keeps your input and output aligned row-for-row. |
| `proxyConfiguration` | disabled | Usually unnecessary. See the FAQ. |

#### Why `expandTagManager` matters

Most sites no longer put ad pixels in their HTML. They load them through Google Tag Manager, and Shopify stores route them through a sandboxed Web Pixels Manager — so `fbq(` never appears in the markup and naive scrapers report nothing.

This Actor reads the site's **GTM container** and extracts the tags configured inside it. In testing, that lifted ad-pixel coverage from **4% to 45% of sites**. It only fires on domains that actually use GTM.

#### Choosing `maxHtmlKb`

Contact details live in page footers, so capping the HTML too low costs you data:

| `maxHtmlKb` | E-mails found | Phones found |
|---|---|---|
| 400 | 37% | 11% |
| **1000** *(default)* | **59%** | **22%** |
| 2500 | 63% | 26% |

1000 KB is the sweet spot. Lower it if you only care about technographics.

***

### Output

One record per input domain, nested JSON by default. Abbreviated:

```json
{
  "input": "gymshark.com",
  "domain": "gymshark.com",
  "url": "https://www.gymshark.com/",
  "success": true,
  "site": { "companyName": "Gymshark", "language": "en-gb", "currencies": ["GBP"] },
  "technologies": [
    { "name": "Shopify", "category": "ecommerce", "version": null,
      "confidence": 100, "evidence": "header: x-shopid",
      "website": "https://www.shopify.com" }
  ],
  "technologiesByCategory": {
    "ecommerce": ["Shopify"], "email-marketing": ["Klaviyo"], "reviews": ["Judge.me"]
  },
  "trackingIds": { "metaPixel": ["1234567890123456"], "googleAnalytics4": ["G-ABCD123456"], "adPixelCount": 3 },
  "contacts": { "emails": ["hello@gymshark.com"], "roleEmails": ["hello@gymshark.com"], "phones": [] },
  "socialProfiles": { "instagram": "https://www.instagram.com/gymshark", "count": 6 },
  "emailInfrastructure": { "provider": "Google Workspace", "dmarcPolicy": "reject",
                           "detectedSenders": ["Klaviyo", "SendGrid"], "isSpoofable": false,
                           "securityScore": 95 },
  "signals": { "runsPaidAds": true, "martechMaturityScore": 78,
               "highlights": ["Runs Shopify", "Advertises on Meta, TikTok", "E-mail via Klaviyo"] }
}
```

Set `flattenOutput: true` for one flat row of dot-notation columns with arrays joined by `" | "` — ready for CSV or a spreadsheet.

Failed domains still produce a row (`success: false`, plus an `errorCode`), so your output stays aligned with your input list. Set `skipFailedDomains: true` if you'd rather drop them.

The dataset ships with ready-made views — **Overview**, **Lead list**, **Tech stack**, **Ad pixels** and **E-mail infrastructure** — so you can export just the columns you need without writing a transformation.

***

### What to expect

Typical coverage per successfully profiled domain:

Measured on a 60-domain sample weighted toward large enterprise retailers and bot-protected sites, at default settings:

| Field | Populated |
|---|---|
| Company name | 98% |
| DNS / MX / DMARC | 100% / 100% / 100% |
| Social profile | 87% |
| E-mail address | 63% |
| Ad pixel IDs | 50% |
| Phone number | 28% |
| **Profiled successfully** | **90%** |

Coverage depends on the site: a small business with a footer full of contact details fills nearly every field, while a large enterprise site that renders everything client-side fills fewer.

The 10% that failed were dead domains and sites behind aggressive bot management that return 403 to any non-browser client. **Domains that fail are never charged.**

#### Accuracy

A false positive in a lead list is worse than a gap — it silently poisons your segmentation. Fingerprints are anchored on vendor-owned hostnames, cookie names and script paths, never on a bare product name, so a page that merely *mentions* thirty vendors on an integrations page returns zero detections.

Every detection includes an `evidence` field naming exactly what matched, so you can verify any row yourself.

**Known limits:**

- Content rendered purely client-side is invisible — there is no browser. This is the deliberate trade-off that makes the Actor fast and cheap.
- Sites behind aggressive bot management (Cloudflare Bot Management, DataDome, Akamai) block plain HTTP clients.
- Pixel IDs are recovered from page markup, GTM containers and Shopify's cart-page pixel config. Sites that dispatch ad tags **server-side** through a CDP (mParticle, Segment, Tealium) or Shopify's sandboxed pixel runtime keep the IDs in a remote config that no HTTP client can read — a browser extension sees them only because it observes the page after that config loads. For those sites `runsPaidAds` is `false` but **`signals.usesServerSideTagging` is `true`**, and the highlights name the dispatcher. Filter on `runsPaidAds || usesServerSideTagging` to catch every advertiser.
- `productCount` is only reported when a page states one outright, rather than guessed from link counts.

***

### Pricing

Pay per event. You are charged only for domains that were **successfully profiled**, and only for the modules you enabled.

| Event | Price | When |
|---|---|---|
| Actor start | $0.005 | Once per run |
| Domain profiled | $0.0025 | Per successful domain — tech stack, tracking IDs, metadata, commerce, hosting, DNS, security headers, pages |
| Contact enrichment | $0.002 | Per domain when contact extraction runs |
| Intelligence enrichment | $0.0015 | Per domain when e-mail infrastructure, GTM expansion or TLS inspection runs |

**Everything enabled: $6 per 1,000 domains. Technographics only: $2.50 per 1,000.**

***

### Tips

- **Chain it.** Point `sourceDatasetId` at the output of a Google Maps, LinkedIn or directory scraper to enrich those companies in place.
- **Schedule it.** Re-run a saved list weekly and diff `technologyNames` to catch platform migrations the week they happen — that is a sales trigger.
- **Segment on `martechMaturityScore`.** Low score + e-commerce platform = a company that hasn't bought tooling yet. High score + `isSpoofable` = a sophisticated buyer with a real problem.
- **Use SAN records.** With `enrichTls` on, `tls.altNames` often reveals every brand and country site a company operates, from a single domain.

***

### FAQ

**Why did some domains fail?**
Check `errorCode`. `ENOTFOUND` means the domain doesn't resolve, `HTTP_403` and `BOT_CHALLENGE` mean bot protection blocked the request, `ETIMEDOUT` means the server didn't respond in time. Failed domains are never billed.

**Do I need a proxy?**
Usually not, and it's disabled by default. Datacenter proxies can help if you're profiling many domains on the same host. Residential proxies raise the success rate on heavily protected sites but cost far more than everything else in this Actor combined — enable them only for lists you know are blocking you.

**How do I get a clean CSV?**
Set `flattenOutput: true`, then export the dataset as CSV. Arrays come through pipe-delimited instead of as JSON blobs.

**Can I profile subpages, not just homepages?**
Yes — pass full URLs. The path is preserved, and tracking parameters are stripped so duplicates still collapse correctly.

**How many domains can one run handle?**
Tens of thousands. Assign more memory and raise `maxConcurrency` together (~20 per GB). Cost per domain is the same at any size; only wall-clock time changes.

**How fresh is the data?**
Every field is fetched live at run time. Nothing is cached or served from a database.

# Actor input Schema

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

Domains or URLs to profile — one per line. Bare domains ('stripe.com'), full URLs and even e-mail addresses ('jane@acme.io') are all accepted. Duplicates and www/apex variants of the same site are merged automatically so you are never charged twice for one company.

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

Alternative to Domains. Use this when feeding output from another Actor straight into this one.

## `sourceDatasetId` (type: `string`):

Enrich an existing dataset in place of a pasted list. Every item's domain field is read and profiled.

## `sourceDatasetField` (type: `string`):

Which field on the source dataset holds the domain or URL.

## `enrichContacts` (type: `boolean`):

Fetch the contact / about / imprint pages and pull e-mail addresses, phone numbers, postal addresses and social profile URLs. Roughly doubles the number of populated fields on a typical record. Billed as a Deep enrichment event.

## `enrichEmailInfrastructure` (type: `boolean`):

Identify the mailbox provider, every third-party sender authorised in SPF, the DMARC policy and which DKIM selectors resolve. Produces a 0-100 e-mail security score and a spoofable flag. Pure DNS — fast and cheap. Billed as a Deep enrichment event.

## `expandTagManager` (type: `boolean`):

Fetch the site's GTM container and read the advertising tags configured inside it. Strongly recommended: most sites now load Meta, TikTok and Google Ads pixels through GTM, so without this their IDs are invisible. In testing this raised ad-pixel coverage from 4% to 41% of sites. Only runs on domains that actually use GTM. Billed as a Deep enrichment event.

## `enrichTls` (type: `boolean`):

Read the live certificate — issuer, expiry, and the Subject Alternative Names that reveal a company's other brands and staging hosts. Off by default because it adds a TLS handshake and one request per domain. Billed as a Deep enrichment event.

## `maxExtraPages` (type: `integer`):

How many contact/about/imprint pages to fetch when contact extraction is on. 0 restricts extraction to the homepage.

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

Domains processed in parallel. Raise it with the memory you assign the run: ~20 per 1 GB is a good rule of thumb. Too high on a small container just queues work behind the CPU.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout. Lower it to drop slow domains faster on very large lists.

## `maxRetries` (type: `integer`):

Retries for timeouts, connection resets and 5xx responses. Redirects and 4xx are never retried.

## `maxHtmlKb` (type: `integer`):

Hard download cap per page. The default of 1000 KB is the measured knee of the curve: dropping to 400 KB cuts bandwidth by a third but also cuts e-mail coverage from 59% to 37%, because contact details live in page footers.

## `flattenOutput` (type: `boolean`):

Emit one flat row of dot-notation columns per domain, with arrays joined by ' | '. Use this when exporting to CSV or loading into a spreadsheet. Leave off for nested JSON, which is better for BigQuery, Snowflake and pandas.

## `skipFailedDomains` (type: `boolean`):

By default every domain produces a row, with success:false and an error code, so your input list and output stay aligned. Turn this on to keep only reachable sites. Failed domains are never charged either way.

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

Optional. Datacenter proxies are usually enough and are effectively free. Residential proxies raise the success rate on heavily protected sites but cost far more than everything else in this Actor combined — enable them only for lists you know are blocking you.

## Actor input object example

```json
{
  "domains": [
    "gymshark.com",
    "stripe.com",
    "notion.so"
  ],
  "sourceDatasetField": "domain",
  "enrichContacts": true,
  "enrichEmailInfrastructure": true,
  "expandTagManager": true,
  "enrichTls": false,
  "maxExtraPages": 2,
  "maxConcurrency": 40,
  "requestTimeoutSecs": 20,
  "maxRetries": 1,
  "maxHtmlKb": 1000,
  "flattenOutput": false,
  "skipFailedDomains": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One dataset item per domain. Failed domains are included with success:false and an errorCode unless skipFailedDomains is enabled.

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

// Run the Actor and wait for it to finish
const run = await client.actor("eiv/website-intelligence-scraper").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": [
        "gymshark.com",
        "stripe.com",
        "notion.so",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("eiv/website-intelligence-scraper").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": [
    "gymshark.com",
    "stripe.com",
    "notion.so"
  ]
}' |
apify call eiv/website-intelligence-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/47Qnw1ufant4RRL7j/builds/X182e4KPlRBCM0EBX/openapi.json
