# Website Tech Stack Detector - CMS, Shopify, Email, CRM & CDN (`artificially/website-tech-stack-detector`) Actor

Detect any website's tech stack like BuiltWith or Wappalyzer: CMS, Shopify/ecommerce, analytics, ads, CDN, hosting, email provider, CRM, chat and payments from 7,000+ fingerprints plus DNS (MX, SPF, TXT). Every detection has evidence. Pay only for domains with detections.

- **URL**: https://apify.com/artificially/website-tech-stack-detector.md
- **Developed by:** [Artificially](https://apify.com/artificially) (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 $10.50 / 1,000 domain with technologies

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

## Website Tech Stack Detector – BuiltWith & Wappalyzer-Style Technology Lookup

Find out **what any company website is built with**: CMS, ecommerce platform (Shopify, WooCommerce, Magento…), analytics, advertising pixels, tag managers, CDN, hosting, **email provider**, CRM and marketing automation, live chat, payment processors, JavaScript frameworks and 7,000+ other technologies. Paste domains, get **one clean item per domain** with every detection backed by **evidence**, and pay only for domains where something was detected.

**Built by [Artificially](https://apify.com/artificially)** | Follow us on [X @artificially\_io](https://x.com/artificially_io)

#### Use with AI agents (MCP)

This actor works as a tool for Claude, ChatGPT, Cursor and other AI agents through [Apify's MCP server](https://mcp.apify.com). Ask something like "which of these 20 domains run on Shopify and which email provider do they use?" and the agent calls it for you. Set `compactOutput: true` so each result is a short list of technology names plus highlights that fits easily in the agent's context.

**Quick setup** (sign in with your Apify account when asked):

- **Claude** (claude.ai or Claude Desktop): Settings → Connectors → Add custom connector, and paste `https://mcp.apify.com?tools=artificially/website-tech-stack-detector`
- **Claude Code or Cursor** via the Apify CLI (latest version, `apify upgrade`): `apify mcp install claude-code --tools artificially/website-tech-stack-detector` (use `cursor` instead of `claude-code` for Cursor)
- **Any MCP client** (Cursor, VS Code, Windsurf):

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=artificially/website-tech-stack-detector" }
  }
}
```

**Try asking:**

- "Which of these 100 websites run on Shopify, and which email provider do they use?"
- "What CMS, analytics and CRM does hubspot.com use?"

### Why this actor

- **Three detection sources combined** – the HTML, scripts, meta tags, cookies and response headers of the home page and up to 3 key pages, plus the domain's **public DNS records** (MX, SPF, TXT verifications, DMARC, nameservers, CNAME). DNS finds tools that never appear on the website: Google Workspace vs Microsoft 365, HubSpot, Salesforce, Mailchimp, SendGrid, Atlassian, Stripe, Zoom, DocuSign, OpenAI and more.
- **Evidence for every detection** – each technology lists what matched: `header: server: cloudflare`, `script: https://cdn.shopify.com/...`, `dns: MX aspmx.l.google.com`, `meta: generator: WordPress 6.6`. No black box; you can verify every line.
- **Versions and confidence** – versions are extracted where the site exposes them (e.g. `WordPress 6.9.9`, `jQuery 3.5.1`, `Yoast SEO 25.1`), and weak hints (like a domain merely allowed in a Content-Security-Policy header) get a low confidence and are filtered out by default.
- **Highlights you can use directly** – `cms`, `ecommerce`, `analytics[]`, `advertising[]`, `tagManagers[]`, `cdn`, `hosting`, `emailProvider`, `frameworks[]`, `chat[]`, `crm[]`, `payment[]`. Hosting and CDN come only from real infrastructure evidence (headers, cookies, CNAME), never from a DNS verification record.
- **Lead filters** – `onlyDomainsUsing: ["Shopify"]` keeps only Shopify stores (non-matching domains are free), and `categories` keeps only the categories you care about.
- **Fast and cheap** – plain HTTP requests (no browser), about 6 requests and 3 seconds per domain, 10 domains in parallel with polite per-site limits.
- **You only pay for results** – a domain is saved and charged only if at least one technology was detected (and matched your filters). Unreachable, parked, disallowed and empty domains are listed for free in `FAILED_DOMAINS`.

### Use cases

- **Shopify / ecommerce lead lists** – filter a list of brands down to the stores that run on Shopify, WooCommerce or BigCommerce, with their payment options and marketing tools.
- **B2B sales prospecting** – find companies on Google Workspace vs Microsoft 365, on HubSpot vs Salesforce, or using a competitor's tool; then get their contacts with the [Company Contact Extractor](https://apify.com/artificially/company-contact-extractor).
- **Market and competitor research** – measure the adoption of a CMS, analytics tool or CDN across an industry.
- **Account enrichment** – add tech stack, email provider and hosting to CRM records, next to firmographics from the [LinkedIn Company Scraper](https://apify.com/artificially/linkedin-company-scraper).
- **Security and IT audits** – see which SaaS vendors, email security gateways (Proofpoint, Mimecast) and DMARC services a domain relies on.

### How detection works

| Source | What is read | Examples |
|--------|--------------|----------|
| HTML and DOM | Markup patterns and CSS selectors | WordPress, Webflow, Contentful, Swiper, PayPal buttons |
| Scripts | Script URLs and inline script code | Google Tag Manager, Shopify, Klaviyo, Hotjar, Stripe.js |
| Meta tags | `generator`, verification and platform tags | WordPress 6.6, Jekyll, Shopify, Bing/Google/Yandex verification |
| Headers and cookies | Response headers and `Set-Cookie` names | Cloudflare, Vercel, Next.js, Nginx, CloudFront, `_shopify_y`, `__cf_bm` |
| URLs | Final URL and redirect chain | `*.myshopify.com`, `*.github.io`, `*.gitbook.com` |
| DNS | MX, TXT (SPF + verifications), DMARC, NS, SOA, CNAME | Google Workspace, Microsoft 365, Proofpoint, HubSpot, Salesforce, SendGrid, Route 53, `shops.myshopify.com` |

`implies`, `excludes` and `requires` relations between technologies are resolved (for example Next.js implies React; WooCommerce requires WordPress), and implied technologies carry `implied` evidence naming the parent.

Technologies that can only be detected by running the page's JavaScript in a real browser (JavaScript global variables) are not checked directly, which keeps the actor fast and cheap. Most of them are still found through their script URLs, markup or `implies` relations.

The fingerprint patterns come from the open-source community project [webappanalyzer](https://github.com/enthec/webappanalyzer) (the maintained continuation of Wappalyzer's open-source fingerprints, licensed GPL-3.0), extended with Artificially's own DNS, header and CNAME rules.

### Input

| Field | Type | Description | Default |
|-------|------|-------------|---------|
| `websites` | array | Domains or URLs, e.g. `allbirds.com`, `www.stripe.com`, `https://vercel.com` | Required |
| `maxPagesPerDomain` | integer | Extra internal pages scanned after the home page (0–10): pricing, careers, shop, contact, demo, blog… | `3` |
| `includeDns` | boolean | Read MX, SPF, TXT, DMARC, NS and CNAME records | `true` |
| `categories` | array | Keep only these categories, e.g. `["Ecommerce", "CMS", "Analytics"]` | all |
| `onlyDomainsUsing` | array | Save only domains using one of these technologies, e.g. `["Shopify"]` | none |
| `minConfidence` | integer | Drop detections below this confidence (0–100) | `50` |
| `compactOutput` | boolean | Slim items: domain, technology names, highlights, emailProvider. Best for AI agents | `false` |
| `respectRobotsTxt` | boolean | Skip pages disallowed by robots.txt | `true` |
| `maxConcurrency` | integer | Websites scanned in parallel (1–50) | `10` |
| `proxyConfiguration` | object | Optional Apify Proxy for sites with bot protection | none |

#### Example input

```json
{
    "websites": ["allbirds.com", "gymshark.com", "stripe.com", "hubspot.com", "stihl.de"],
    "maxPagesPerDomain": 3,
    "includeDns": true,
    "minConfidence": 50
}
```

Shopify store leads only:

```json
{
    "websites": ["allbirds.com", "gymshark.com", "stripe.com", "yoast.com"],
    "onlyDomainsUsing": ["Shopify"],
    "compactOutput": true
}
```

### Output

One item per domain (technologies shortened here):

```json
{
    "input": "allbirds.com",
    "domain": "allbirds.com",
    "websiteUrl": "https://www.allbirds.com/",
    "finalUrl": "https://www.allbirds.com/",
    "siteStatus": "ok",
    "title": "Allbirds: Comfortable, Sustainable Shoes & Apparel",
    "technologies": [
        {
            "name": "Shopify",
            "slug": "shopify",
            "categories": ["Ecommerce"],
            "version": null,
            "confidence": 100,
            "website": "https://shopify.com",
            "evidence": [
                { "type": "header", "value": "powered-by: Shopify" },
                { "type": "script", "value": "https://cdn.shopify.com/s/files/1/1104/4168/files/jsEncrypt.js?v=1756910700" },
                { "type": "meta", "value": "shopify-digital-wallet: /11044168/digital_wallets/dialog" }
            ]
        },
        {
            "name": "Microsoft 365",
            "slug": "microsoft-365",
            "categories": ["Webmail", "Email"],
            "version": null,
            "confidence": 100,
            "website": "https://www.microsoft.com/microsoft-365",
            "evidence": [
                { "type": "dns", "value": "MX allbirds-com.mail.protection.outlook.com" },
                { "type": "dns", "value": "TXT v=spf1 include:allbirds_com._es.easydmarc.com include:spf.protection.outlook.com ~all" }
            ]
        },
        {
            "name": "Cloudflare",
            "slug": "cloudflare",
            "categories": ["CDN"],
            "version": null,
            "confidence": 100,
            "website": "https://www.cloudflare.com",
            "evidence": [{ "type": "header", "value": "server: cloudflare" }]
        }
    ],
    "byCategory": {
        "CDN": ["Cloudflare"],
        "Ecommerce": ["Shopify"],
        "Email": ["Microsoft 365"],
        "Payment processors": ["Apple Pay", "PayPal"],
        "Tag managers": ["Google Tag Manager"]
    },
    "highlights": {
        "cms": null,
        "ecommerce": "Shopify",
        "analytics": [],
        "advertising": [],
        "tagManagers": ["Google Tag Manager"],
        "cdn": "Cloudflare",
        "hosting": null,
        "emailProvider": "Microsoft 365",
        "frameworks": [],
        "chat": [],
        "crm": [],
        "payment": ["Apple Pay", "PayPal"]
    },
    "dns": {
        "mx": ["allbirds-com.mail.protection.outlook.com"],
        "spfIncludes": ["allbirds_com._es.easydmarc.com", "spf.protection.outlook.com"],
        "nameservers": ["ns2.markmonitor.com", "ns3.markmonitor.com"],
        "verifications": ["MS (Microsoft 365)", "apple-domain-verification", "docusign", "google-site-verification", "miro-verification"],
        "cname": "shops.myshopify.com"
    },
    "pagesScanned": 4,
    "scrapedAt": "2026-09-23T11:26:56.680Z"
}
```

| Field | Description |
|-------|-------------|
| `input` | The website exactly as you entered it |
| `domain` | The registrable domain after redirects (e.g. `notion.so` → `notion.com`) |
| `websiteUrl` / `finalUrl` | The URL that was loaded and the URL after redirects |
| `siteStatus` | `ok`, or `blocked` when the website refused automated requests and the result comes only from DNS and the response headers of the block page |
| `title` | The home page title |
| `technologies` | `{ name, slug, categories[], version, confidence, website, evidence[] }`; evidence `type` is `html`, `script`, `header`, `cookie`, `meta`, `url`, `dns` or `implied` |
| `byCategory` | Technology names grouped by category |
| `highlights` | The most useful facts at a glance (see above). `emailProvider` comes from MX records, or from SPF when mail passes through a security gateway such as Proofpoint or Mimecast |
| `dns` | MX hosts, SPF includes, nameservers, verification record names and the website CNAME. `null` when `includeDns` is off |
| `pagesScanned` | Pages analyzed for this domain |

With `compactOutput: true` an item looks like this:

```json
{
    "domain": "allbirds.com",
    "technologies": ["Apple Business", "Apple Pay", "Cloudflare", "Google Tag Manager", "Microsoft 365", "PayPal", "Shopify", "Swiper"],
    "highlights": { "cms": null, "ecommerce": "Shopify", "analytics": [], "advertising": [], "tagManagers": ["Google Tag Manager"], "cdn": "Cloudflare", "hosting": null, "emailProvider": "Microsoft 365", "frameworks": [], "chat": [], "crm": [], "payment": ["Apple Pay", "PayPal"] },
    "emailProvider": "Microsoft 365"
}
```

The key-value store also contains:

- `SUMMARY` – saved and failed counts, average technologies, pages, requests, KB and seconds per domain, the 30 most common technologies, and the fingerprint dataset version.
- `FAILED_DOMAINS` – domains that were not saved, with a reason: `unreachable`, `blocked` (bot protection and no DNS signals; try a proxy), `blocked_by_robots`, `parked_domain`, `not_html`, `invalid_input`, `no_technologies_found`, `no_matching_technology` (did not match `onlyDomainsUsing`). These are never charged.

### Pricing

This actor uses pay-per-event pricing: a tiny fee per run plus a fee per domain saved to the dataset. **You only pay for domains with at least one detected technology** that match your filters – unreachable, parked, empty and non-matching domains cost nothing, and there is no per-page fee. Set a maximum cost per run in Apify Console and the actor stops cleanly when it is reached. See the **Pricing** tab for current prices.

### FAQ

**Which pages are scanned?**
The home page, then up to `maxPagesPerDomain` internal pages linked from it, at most one per type: pricing, careers, shop/collections, contact, demo/signup, blog, support, about, product. Different page types load different tools (checkout and payment widgets, applicant tracking systems, forms, chat). Login pages and other websites are never loaded.

**Why is a technology I know about missing?**
Some tools are loaded only after JavaScript runs, after cookie consent, or only for logged-in users. This actor reads the HTML and headers the server sends, without a browser, so those can be missed. DNS signals often fill the gap for B2B tools (CRM, email, marketing automation).

**What do DNS verification records mean?**
Records such as `google-site-verification`, `facebook-domain-verification`, `atlassian-domain-verification` or `stripe-verification` are added by a company when it connects its domain to that service. They show which SaaS accounts the company has, even when the tool is not visible on the website.

**Why is `hosting` or `cdn` sometimes null?**
They are filled only from infrastructure evidence (response headers, cookies, the website's CNAME). A nameserver at Cloudflare or Route 53 says where the DNS is managed, not where the website runs, so it is listed in `technologies` and `dns.nameservers` but not used for `hosting`.

**Is this legal?**
The actor reads public web pages and public DNS records, the same information any browser or `dig` command sees. It does not log in or bypass protection, and pages disallowed by robots.txt are skipped by default. You are responsible for how you use the data.

### Support

Found a wrong or missing detection? Open an issue on the actor's **Issues** tab with the domain – we usually respond within a day.

### Related actors

- [Company Contact Extractor](https://apify.com/artificially/company-contact-extractor): public emails, phones and social profiles from company websites – get the contacts of the companies you found here.
- [LinkedIn Company Scraper](https://apify.com/artificially/linkedin-company-scraper): company profiles with size, industry, headquarters and website for firmographic enrichment.
- [Career Site Jobs Scraper](https://apify.com/artificially/career-site-jobs-scraper): open jobs from company career sites (Greenhouse, Lever, Ashby, Workday and more) – combine hiring signals with tech stack.

# Actor input Schema

## `websites` (type: `array`):

Required. One company website per entry: a bare domain (e.g. allbirds.com), a domain with www (e.g. www.stripe.com) or a full URL (e.g. https://vercel.com or https://shop.example.com/de to start on a specific page). http/https and www/non-www are resolved automatically and redirects are followed. Example: \["allbirds.com", "stripe.com", "https://www.hubspot.com"].

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

How many important internal pages to scan in addition to the home page, from 0 to 10. The actor picks at most one page per type from the home page links: pricing, careers, shop/collections, contact, demo/signup, blog, support, about, product. Different page types often load different tools (checkout, ATS, forms, chat). Example: 0 for the fastest run (home page only), 3 (default) for good coverage, 6 for deep scans.

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

If true (default), also reads the domain's public DNS records: MX (email provider such as Google Workspace, Microsoft 365, Zoho, Proton), SPF includes and TXT verification records (e.g. HubSpot, Salesforce, Mailchimp, SendGrid, Atlassian, Stripe, Facebook, Zoom, DocuSign), DMARC reporting provider, nameservers (Cloudflare, Route 53, ...) and the website CNAME. Very cheap and a strong B2B signal. Set to false to analyze the website only.

## `categories` (type: `array`):

Optional. Keep only technologies in these categories (case-insensitive, Wappalyzer category names). Leave empty to keep everything. Common names: Ecommerce, CMS, Page builders, Analytics, Advertising, Tag managers, CDN, Hosting, PaaS, Email, Webmail, Marketing automation, CRM, Live chat, Payment processors, JavaScript frameworks, Web frameworks, Cookie compliance, Security, Recruitment & staffing, Reviews, A/B Testing. Example: \["Ecommerce", "CMS", "Analytics"]. Domains with nothing left after filtering are not saved and not charged.

## `onlyDomainsUsing` (type: `array`):

Optional lead filter. Save only domains that use at least one of these technologies (case-insensitive, names as they appear in the output). Domains that do not match are listed in FAILED\_DOMAINS with reason no\_matching\_technology and are not charged. Example: \["Shopify"] for Shopify store leads, \["WordPress", "WooCommerce"], \["HubSpot"], \["Google Workspace"].

## `minConfidence` (type: `integer`):

Drop detections below this confidence (0-100). Each fingerprint pattern carries a confidence and matches are added up per technology. 50 (default) removes weak single hints such as a URL merely allowed in a Content-Security-Policy header. Use 100 for only the most certain detections, 0 to keep everything.

## `compactOutput` (type: `boolean`):

Set to true to save a slim item per domain: domain, technologies (names only), highlights (cms, ecommerce, analytics, advertising, tagManagers, cdn, hosting, emailProvider, frameworks, chat, crm, payment) and emailProvider. Versions, confidence, evidence, DNS records and categories are dropped. Recommended when an AI agent or LLM reads the results. Leave false for the full item.

## `respectRobotsTxt` (type: `boolean`):

If true (default), pages disallowed by the website's robots.txt are not loaded, and websites that disallow everything are reported in FAILED\_DOMAINS with reason blocked\_by\_robots. Set to false only if you have the right to scan those pages.

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

How many websites are scanned at the same time, from 1 to 50. Each website still gets at most 2 requests at a time with a short delay between them. Example: 10 (default).

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

Optional. Most websites work without a proxy. If many domains end up blocked (siteStatus blocked, or reason blocked in FAILED\_DOMAINS), enable Apify Proxy, e.g. {"useApifyProxy": true} for datacenter or {"useApifyProxy": true, "apifyProxyGroups": \["RESIDENTIAL"]} for residential. DNS lookups never use the proxy.

## Actor input object example

```json
{
  "websites": [
    "allbirds.com",
    "stripe.com",
    "apify.com"
  ],
  "maxPagesPerDomain": 3,
  "includeDns": true,
  "minConfidence": 50,
  "compactOutput": false,
  "respectRobotsTxt": true,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

One item per domain with CMS, ecommerce, analytics, advertising, CDN, hosting, email provider, CRM and every detected technology with evidence (overview table view)

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

Saved and failed counts, average technologies, pages, requests, KB and seconds per domain, top technologies and fingerprint dataset version

## `failedDomains` (type: `string`):

Websites that were unreachable, blocked without DNS signals, parked, disallowed by robots.txt, had no detections or did not match onlyDomainsUsing, with a reason (never charged)

# 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 = {
    "websites": [
        "allbirds.com",
        "stripe.com",
        "apify.com"
    ],
    "maxPagesPerDomain": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("artificially/website-tech-stack-detector").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 = {
    "websites": [
        "allbirds.com",
        "stripe.com",
        "apify.com",
    ],
    "maxPagesPerDomain": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("artificially/website-tech-stack-detector").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 '{
  "websites": [
    "allbirds.com",
    "stripe.com",
    "apify.com"
  ],
  "maxPagesPerDomain": 3
}' |
apify call artificially/website-tech-stack-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,artificially/website-tech-stack-detector"
        }
    }
}
```

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/f5GiKc3LDWU7SRct9/builds/CKd5hwTSukhv3hstO/openapi.json
