# Website Tech Stack Detector (Wappalyzer, BuiltWith alternative) (`kilndata/website-tech-stack-detector`) Actor

Detect 7,000+ technologies on any website: CMS, ecommerce platform, analytics, ad pixels, payment, live chat, CDN, hosting, email provider (DNS) and more. Bulk domains, clean JSON with version, confidence and evidence. No API key, no browser, no personal data.

- **URL**: https://apify.com/kilndata/website-tech-stack-detector.md
- **Developed by:** [Datakiln](https://apify.com/kilndata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 website scanneds

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 (Wappalyzer and BuiltWith alternative)

Paste a list of websites or domains and get back the technologies each one uses: CMS, ecommerce platform, analytics, ad pixels, payment tools, live chat, CDN, hosting, JavaScript frameworks and, through DNS, the email provider. Each technology comes with its category, a version when the site shows one, a confidence score and the signal that revealed it.

It makes one HTTP request per website, reads the response and, if you leave the option on, looks up the domain's DNS records. It opens no browser, needs no login and no API key, and collects no personal data.

### Who uses it

| Use | How |
|---|---|
| Sales prospecting (B2B) | Scan a list of leads and keep the ones running Shopify, WooCommerce, HubSpot or whatever you sell to or integrate with |
| SEO and agency audits | See the CMS, analytics, tag managers, CDN and consent tools of a client or a competitor in one row |
| Market research | Scan a few hundred sites in a niche and count which platforms and tools they use, with `technologiesByCategory` |
| Migration and lead lists | Find sites still on an old platform or an old library version |
| Email and DNS checks | See whether a domain's mail runs on Google Workspace or Microsoft 365, and which DNS host it uses |
| Feeding a workflow or AI agent | Call it through the API or MCP and read the JSON rows |

### How it works

For every site the Actor:

1. Normalizes the input (`example.com` becomes `https://example.com`) and removes duplicates.
2. Requests the page once and follows redirects. Only that page is fetched, no links are followed.
3. Reads the final URL, response headers, cookies, the HTML, meta tags, script URLs, inline scripts, inline CSS and page structure.
4. Optionally reads the domain's MX, TXT, NS, SOA and CNAME records.
5. Matches all of that against 7,628 technology fingerprints from the open-source [webappanalyzer](https://github.com/enthec/webappanalyzer) project (GPL-3.0), fetched on 2026-10-05, and adds technologies those fingerprints imply (for example, WooCommerce implies WordPress).

Detection is pattern matching on what the server sends. No JavaScript is executed.

### How to use it

1. Paste websites or domains, one per line. A missing `https://` is added for you.
2. Leave the defaults or narrow the result with `categoryFilter` and `minConfidence`.
3. Start the run and open the dataset. The "Overview" view lists each domain with its technologies, split into what the website shows and what only DNS shows. The "By category" view groups them.

Example input:

```json
{
    "urls": ["mailchimp.com", "https://www.wikipedia.org", "example.com"],
    "checkDns": true,
    "minConfidence": 50,
    "categoryFilter": ["CDN", "Analytics", "Email"]
}
```

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `urls` | array of strings | required | Websites or domains, one per line. `example.com`, `www.example.com` and `https://example.com/page` all work. Duplicates are removed |
| `checkDns` | boolean | `true` | Also look up MX, TXT, NS, SOA and CNAME. Finds email providers, DNS hosts and SaaS domain verifications that the page does not show |
| `categoryFilter` | array of strings | empty | Keep only technologies in these categories, such as `Ecommerce`, `CMS`, `Analytics`, `Payment processors`, `Live chat`. Case does not matter. Empty returns everything |
| `minConfidence` | integer 0 to 100 | `0` | Hide detections below this score. 100 keeps only certain matches |
| `includeEvidence` | boolean | `true` | Add the `detectedBy` list to each technology |
| `respectRobotsTxt` | boolean | `true` | Skip websites whose robots.txt disallows the page. Skipped sites are listed and not charged |
| `maxConcurrency` | integer 1 to 100 | `20` | Websites scanned in parallel |
| `requestTimeoutSecs` | integer 5 to 120 | `30` | How long to wait for a site before retrying |
| `maxRetries` | integer 0 to 5 | `2` | Retries before a site is reported as failed. Domains that do not exist are not retried |
| `proxyConfiguration` | object | no proxy | Optional. Use Apify Proxy if some sites block the requests |

### Output

One row per website. This is a real row from a scan of mailchimp.com, shortened (the full row lists 19 technologies):

```json
{
    "inputUrl": "mailchimp.com",
    "url": "https://mailchimp.com/",
    "domain": "mailchimp.com",
    "status": "ok",
    "httpStatus": 200,
    "title": "Email & SMS Marketing Platform | Mailchimp",
    "language": "en",
    "technologyCount": 19,
    "technologyNames": ["Akamai", "Microsoft 365", "Optimizely", "React"],
    "websiteTechnologyNames": ["Akamai", "Optimizely", "React"],
    "dnsServiceNames": ["Microsoft 365", "Zapier"],
    "technologiesByCategory": {
        "CDN": ["Akamai"],
        "A/B Testing": ["Optimizely"],
        "JavaScript frameworks": ["React"]
    },
    "technologies": [
        {
            "name": "Optimizely",
            "slug": "optimizely",
            "categories": ["A/B Testing", "Personalisation"],
            "version": null,
            "confidence": 100,
            "detectedBy": ["scriptSrc"],
            "website": "https://www.optimizely.com",
            "saas": true,
            "openSource": null,
            "pricing": ["poa", "high"],
            "cpe": null
        }
    ],
    "mxRecords": ["mx1.intuit.iphmx.com", "mx2.intuit.iphmx.com"],
    "nameservers": ["a7-66.akam.net", "a6-66.akam.net"],
    "error": null,
    "scannedAt": "2026-10-05T02:54:06.439Z"
}
```

Fields in plain words:

| Field | Meaning |
|---|---|
| `status` | `ok`, `failed`, `skipped` or `invalid-input`. Only `ok` is charged |
| `technologyNames` | Flat list of everything found |
| `websiteTechnologyNames` | Technologies seen on the website itself: headers, HTML, scripts, cookies |
| `dnsServiceNames` | Services seen only in DNS records, such as the email provider or a SaaS domain verification. Being in DNS means the domain uses or once verified the service, not that the page loads it |
| `technologiesByCategory` | Category name mapped to the technologies in it |
| `technologies[].confidence` | 0 to 100. Lower scores come from weaker patterns |
| `technologies[].detectedBy` | Signals behind the match: `headers`, `html`, `scriptSrc`, `meta`, `cookies`, `dom`, `dns`, `url`, or `implied by <technology>` |
| `technologies[].version` | Only when the site exposes it. Many hide it |
| `error` | Why a site could not be scanned, for `failed` and `skipped` rows |

Rows for failed, skipped and invalid sites keep the same fields with zero technologies, so a filter on `status` is enough to separate them. A run summary (scanned, failed, invalid, fingerprint source) is stored under `SUMMARY` in the key-value store.

### How much does it cost?

You pay per website read successfully, with the `website-scanned` event. It is charged once per site, when the row has `status: ok`.

| Plan | Price per 1,000 websites |
|---|---|
| Free | US$ 6.00 |
| Starter (Bronze) | US$ 5.00 |
| Scale (Silver) | US$ 4.50 |
| Business (Gold) | US$ 4.00 |

There is also an Actor Start fee of US$ 0.00005 per run.

Not charged: websites that fail (DNS error, timeout, connection refused, blocked with 403), websites skipped by robots.txt, lines that are not a valid domain, and duplicates in the list.

You can set a maximum cost per run in the run options. The Actor reads that limit before starting. If your list is longer than the limit allows, it scans only the first sites that fit and writes a warning to the log. If the limit is reached during the run, it stops by itself and keeps the results already saved.

### Limits, read before you run

- Detection uses what the server sends. A site that builds its page with JavaScript after loading can show less than it really runs. The Actor does not execute scripts.
- Only the page you give is read (usually the home page). Tools used only on inner pages, such as a checkout, are not seen.
- Some sites block anything that is not a browser. They come back as `failed` with a 403 message, and you are not charged. The Actor does not try to get around that. In our test of 25 sites, one (nytimes.com) was blocked this way.
- Fingerprints are community patterns. They can miss a technology, and a few can match something that is not there. Use `confidence` and `detectedBy` to judge a result, and `minConfidence` to cut weak ones. Scripts, headers and cookies are strong signals. A match from HTML text alone is weaker.
- Versions appear only when the site exposes them. A missing version does not mean the software is current.
- DNS results depend on the records the owner published. A TXT verification can remain after the company stopped using that service.
- Pages are read up to about 600,000 characters of HTML. Larger pages are cut.
- The fingerprint base is a snapshot (2026-10-05). New tools appear after that date and will not be detected until the base is updated.
- Rows come out in the order scans finish, not in input order.

### Responsible use

The Actor reads public pages and public DNS records. It does not log in, fill forms or solve captchas, and it does not send cookies from one site to another. It stores no personal data: the output describes technologies, not people. With `respectRobotsTxt` on (the default), it skips pages that robots.txt disallows. You are responsible for how you use the results, including contacting businesses you find with them, under the rules that apply to you (for example GDPR, CAN-SPAM, LGPD) and each site's terms.

### FAQ

**Does it run a browser?**
No. It makes a plain HTTP request and reads the response, which keeps it fast and cheap. The trade-off is in Limits: it cannot see what a script adds after the page loads.

**Why did a site come back as `failed` and what do I pay?**
The domain may not exist, may time out, or may block bots. Failed rows are in the dataset with the reason in `error`, and cost nothing.

**Can I scan a specific page and not the home page?**
Yes. Give the full URL, such as `https://example.com/pricing`. That one page is read. DNS is always checked on the domain.

**What is the difference between `websiteTechnologyNames` and `dnsServiceNames`?**
The first list is what the page, headers and cookies show. The second is what only DNS records show, mostly the email provider and SaaS domain verifications. Both are in `technologyNames`.

**Can I find only sites using a given platform?**
Run the list, then filter the dataset on `technologyNames` or `technologiesByCategory` in your tool or with the API. The Actor does not discover sites for you, it scans the ones you give it.

**Is the data live?**
Each run fetches the sites at that moment. Nothing is cached between runs. Schedule the Actor if you want to track changes over time.

**How many sites can I scan in one run?**
There is no fixed cap in the Actor. Your limit is your maximum cost per run and your plan's memory. Raise `maxConcurrency` for large lists, and lower it if many sites fail.

### Feedback and feature requests

Found a technology that is missed or wrongly detected, or want a new field or option? Open an issue in the Issues tab of this Actor. Include the website and what you expected to see, and I will check it against the fingerprints.

# Actor input Schema

## `urls` (type: `array`):

Websites or domains to scan, one per line. "example.com", "www.example.com" and "https://example.com/page" all work. Duplicates are removed automatically.

## `checkDns` (type: `boolean`):

Also look up MX, TXT, NS, SOA and CNAME records. Detects email providers (Google Workspace, Microsoft 365), DNS hosts and SaaS domain verifications that are not visible on the page.

## `categoryFilter` (type: `array`):

Optional. Keep only technologies in these categories, e.g. "Ecommerce", "CMS", "Analytics", "Payment processors", "Live chat", "Marketing automation". Leave empty to return everything.

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

Hide detections below this confidence score. 0 returns everything; 100 returns only certain matches.

## `includeEvidence` (type: `boolean`):

Add a "detectedBy" list to each technology (headers, html, scriptSrc, meta, cookies, dom, dns, url...), so you can see why it was detected.

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

Skip websites whose robots.txt disallows fetching the page. Skipped websites are listed in the results and not charged.

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

How many websites are scanned in parallel.

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

How long to wait for a website to respond before retrying.

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

How many times a failing website is retried before it is reported as failed (failed websites are not charged).

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

Optional. Most websites work without a proxy. Use Apify Proxy if some websites block the requests.

## Actor input object example

```json
{
  "urls": [
    "https://www.shopify.com",
    "https://wordpress.org",
    "https://www.bbc.com"
  ],
  "checkDns": true,
  "categoryFilter": [],
  "minConfidence": 0,
  "includeEvidence": true,
  "respectRobotsTxt": true,
  "maxConcurrency": 20,
  "requestTimeoutSecs": 30,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "urls": [
        "https://www.shopify.com",
        "https://wordpress.org",
        "https://www.bbc.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kilndata/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 = { "urls": [
        "https://www.shopify.com",
        "https://wordpress.org",
        "https://www.bbc.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("kilndata/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 '{
  "urls": [
    "https://www.shopify.com",
    "https://wordpress.org",
    "https://www.bbc.com"
  ]
}' |
apify call kilndata/website-tech-stack-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kilndata/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/NwVQYOTefMWb5WHI0/builds/ee53fiBxpssOfQsTJ/openapi.json
