# Website Technology Detector (Wappalyzer Alternative) (`datalantern/tech-stack-detector`) Actor

Identify CMS, ecommerce, analytics and payment technologies from public website URLs. Compare prospects or competitors in CSV/JSON, with evidence and warnings. No browser JavaScript execution. Start with the 3-site example in Example tasks.

- **URL**: https://apify.com/datalantern/tech-stack-detector.md
- **Developed by:** [Data Lantern](https://apify.com/datalantern) (community)
- **Categories:** Lead generation, SEO tools, Developer tools
- **Features:** Limited permissions
- **Stats:** 18 total users, 9 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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 Technology Detector (Wappalyzer Alternative)

**Website Technology Detector** finds out what any website is built with. Enter a list of websites and get each site's tech stack: CMS, ecommerce platform, analytics and advertising tools, payment providers, CDN, JavaScript frameworks, web server, and more, with versions where the site reveals them. It is a bulk Wappalyzer alternative for lead generation, sales research, and competitor analysis.

### What can this tool do?

- Detect more than 6,700 technologies from the open-source Wappalyzer fingerprint database, plus our own maintained rules for modern frameworks and payment providers (Next.js app router, Nuxt, VitePress, Creem, Lemon Squeezy, Paddle, Stripe Checkout, Polar, Clerk, Framer).
- Identify the CMS (WordPress, Drupal, Webflow, Wix …) and ecommerce platform (Shopify, WooCommerce, Magento …).
- List analytics, advertising, tag manager, payment, live chat, and marketing tools.
- Report versions when the website exposes them (for example `WordPress 7.2`).
- Read HTTP headers, cookies, meta tags, script URLs, page HTML, the site's own JavaScript files, and DNS records (email provider and DNS host).
- Show the evidence behind every detection, and keep weak signals (like a host that only appears in a Content-Security-Policy header) out of the summary columns.
- Fill ready-made summary columns (`cms`, `ecommerce`, `cdn`, `analytics` …) for easy filtering in Excel or Google Sheets.
- Analyse thousands of websites in one run; sites that fail to load or block the request are listed with the reason and not charged.

### What data does it return?

One row per website. Example values from a real run of the default input:

| Field | wordpress.org | shopify.com | apify.com |
|---|---|---|---|
| `url` | https://wordpress.org/ | https://www.shopify.com/ | https://apify.com/ |
| `finalUrl` | https://wordpress.org/ | https://www.shopify.com/ | https://apify.com/ |
| `statusCode` | 200 | 200 | 200 |
| `cms` | WordPress | null | null |
| `ecommerce` | null | Shopify | null |
| `cdn` | null | Cloudflare | Amazon CloudFront |
| `frameworks` | \[] | \["Tailwind CSS"] | \["Next.js", "React", "styled-components"] |
| `analytics` | \[] | \[] | \[] |
| `advertising` | \[] | \[] | \[] |
| `payment` | \[] | \[] | \[] |
| `technologyNames` | \["Google Font API", "Google Tag Manager", "Gutenberg", "HSTS", "MySQL", "Nginx", "Open Graph", "PHP", …] (13) | \["Cart Functionality", "Cloudflare", "Google Workspace", "HSTS", "HTTP/3", "Open Graph", "Priority Hints", "Shopify", …] (9) | \["Amazon CloudFront", "Amazon Route 53", "Amazon Web Services", "Google Workspace", "HSTS", "HTTP/3", "Next.js", "Open Graph", …] (13) |
| `technologies` | \[{"name": "WordPress", "categories": \["CMS", "Blogs"], "version": "7.2", "confidence": 100, "evidence": \["dom", "header: link", "html", "inline script", "meta: generator", "script URL"]}, …] | \[{"name": "Cloudflare", "categories": \["CDN"], "version": null, "confidence": 100, "evidence": \["header: cf-cache-status", "header: cf-ray", "header: server"]}, …] | \[{"name": "Algolia", "categories": \["Search engines"], "version": null, "confidence": 25, "evidence": \["header: content-security-policy"]}, …] |
| `error` | null | null | null |
| `detectedAt` | 2026-09-24T20:16:35.063Z | 2026-09-24T20:16:35.063Z | 2026-09-24T20:16:35.063Z |

Each entry in `technologies` also has `evidence`, for example `["script URL", "header: x-nextjs-prerender"]` or `["implied by Next.js"]`. Summary columns and `technologyNames` only include confident detections (confidence 50 or more).

Every row has the same fields, so CSV and Excel exports stay tidy. The **Technologies with versions** view shows one line per technology.

`analysisWarnings` lists HTML sampling or JavaScript byte limits reached during analysis. Check it alongside `error`: a warning means some signals may have been missed, not that the website failed to load.

Script scanning considers up to 20 same-host files, reads at most the first 1 MB of each, and analyses at most 2 MB of script text per website. A request stops reading when its prefix limit is reached; files already in flight can make total transferred bytes exceed the analysis budget. This does not execute JavaScript or guarantee detection of every technology.

### How to use it

1. Enter websites, one per line (domains or full URLs).
2. Optionally pick categories to keep, for example only **Ecommerce** and **Payment processors**.
3. Click **Start**, then download the results as CSV, Excel, or JSON, or read them through the API.

### Common uses

- Build lead lists: find every Shopify or WooCommerce store in a list of domains.
- Sales research: see which analytics, chat, or payment tools a prospect already uses.
- Competitor and market analysis: compare the stacks of the top sites in a niche.
- Agency audits: check a client's CMS and plugin versions before a project.

### Pricing

Pay per website: **$3.00 per 1,000 websites**, plus $0.005 per run, charged once the first website has loaded. Websites that cannot be loaded or that block the request are listed with the reason and not charged; a run where no website loads fails and costs nothing. Set a maximum cost per run in the run options.

### Use it through the API, AI agents, and integrations

Call it from your code with the Apify API, schedule it, or connect it to Make, Zapier, n8n, or an AI agent through Apify's MCP server.

### Is it legal?

This tool loads each website's public home page once, the way a browser does, and reads technical signals such as headers and script URLs. It does not log in, bypass access controls, or collect personal information. Technology fingerprints come from the open-source [webappanalyzer](https://github.com/enthec/webappanalyzer) project (GPL-3.0). You are responsible for how you use the data.

### FAQ

**Why is a technology I know about missing?** The tool reads the page without running JavaScript, so tools that only appear after scripts run, or that load on inner pages, can be missed. Enter the exact page you care about (for example a checkout or blog page) to analyse it directly.

**A website returned HTTP 403. What can I do?** Some websites block cloud servers. The row still shows what the block page reveals (often the CDN or firewall), and it is not charged. Turn on the **Proxy** option and run those sites again; this helps for some sites, while heavily protected ones may still refuse automated requests.

**Why is a technology in `technologies` but not in the summary columns?** Some signals are weak. For example, a Content-Security-Policy header often still lists services a site no longer uses (like `*.paypal.com`), so a technology seen only there gets confidence 25 and the evidence `header: content-security-policy`, and it is left out of `payment`, `analytics` and the other summary columns.

**How accurate is it?** On our test set of 27 websites with known stacks (Next.js, Nuxt, Astro, SvelteKit, WordPress, Shopify, Wix, Webflow, Framer, Laravel, Hugo and more), the latest cloud run found 46 of 46 expected technologies with 0 false positives out of 54 checks; 1 site blocked the request with a bot check. Real-world sites vary, so treat results as strong hints, not proof.

**What does `confidence` mean?** 100 means a definite signal was found. Lower values mean a weaker signal, for example a technology implied by another one.

**How does it handle slow or very large websites?** Each home-page request has a 45-second timeout and may be retried once. For HTML larger than 2 MB, the tool analyses the first 1.5 MB and last 0.5 MB; `analysisWarnings` explains that signals in the omitted middle may be missed. Script-content scanning is limited to 20 same-host files, 1 MB per file and 2 MB of retained script content per website, within a 20-second script-fetch stage. Once that content budget is used, queued scripts are not requested; requests already in flight may still finish. Bot-check pages are not followed by script downloads. These limits reduce wasted work, but they are not a promise that a complete batch will finish within a particular time.

**Can I make a lighter scan?** Set `scanScripts` to `false` to skip downloading JavaScript file contents. The tool still reads page HTML, script URLs, headers, cookies and DNS signals, but technologies found only inside a JavaScript bundle may be missed.

### Feedback

Found a bug, a wrong detection, or need a field added? Open an issue on the **Issues** tab. We usually respond within 24 hours.

### Compare website technology stacks

Use this example to turn a list of competitor or prospect websites into a technology comparison. Open the Actor's Input tab, switch to JSON, and paste:

```json
{
  "urls": [
    "https://apify.com",
    "https://www.shopify.com",
    "https://wordpress.org"
  ],
  "maxResults": 3,
  "includeCategories": [],
  "scanScripts": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

Replace the URLs with your own list. After the run, export CSV or Excel and compare `cms`, `ecommerce`, `cdn`, `frameworks`, `analytics`, and `payment`. Check `error` and `statusCode` before using a row. An empty field means no confident detection, not proof that a website does not use that technology.

At the current listed rates, a run with three successfully analysed websites costs **$0.014 in Actor event charges**: $0.005 per start plus 3 × $0.003. Failed or blocked websites do not incur the website event charge; the start charge still applies.

#### Copyable API request

Set `APIFY_TOKEN` securely in your local environment. This request starts a paid run; it does not merely preview the input. It caps the run charge at $0.05 and prints the JSON results.

```bash
curl --fail-with-body --silent --show-error --max-time 320 \
  --request POST \
  --header "Authorization: Bearer ${APIFY_TOKEN}" \
  --header 'Content-Type: application/json' \
  --data '{"urls":["https://apify.com","https://www.shopify.com","https://wordpress.org"],"maxResults":3,"includeCategories":[],"scanScripts":true,"proxyConfiguration":{"useApifyProxy":false}}' \
  'https://api.apify.com/v2/actors/datalantern~tech-stack-detector/run-sync-get-dataset-items?format=json&clean=true&timeout=240&maxTotalChargeUsd=0.05&restartOnError=false'
```

For a larger list, raise `maxResults` and choose an appropriate charge cap. The synchronous endpoint waits at most 300 seconds. If the connection times out, inspect the existing run in Apify Console before retrying: a timeout does not guarantee the run has stopped, and another POST starts another run.

#### Run larger batches asynchronously

For larger batches, use the asynchronous `/runs` endpoint instead of holding an HTTP connection open for all results. This request starts a paid run and returns its run metadata, not the dataset:

```bash
curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Authorization: Bearer ${APIFY_TOKEN}" \
  --header 'Content-Type: application/json' \
  --data '{"urls":["https://apify.com","https://www.shopify.com","https://wordpress.org"],"maxResults":3,"includeCategories":[],"scanScripts":true,"proxyConfiguration":{"useApifyProxy":false}}' \
  'https://api.apify.com/v2/actors/datalantern~tech-stack-detector/runs?maxTotalChargeUsd=0.05'
```

Save `data.id` from that response. Read `GET https://api.apify.com/v2/actor-runs/<run-id>` with the same Authorization header until the run finishes. Its `data.defaultDatasetId` identifies the dataset; download it with `GET https://api.apify.com/v2/datasets/<dataset-id>/items?format=json&clean=true`. Check the final run status before treating a partial dataset as a complete batch. If a start request loses its connection, check Apify Console for the existing run before sending another POST.

#### Recover a partly completed batch

Keep the original input and run ID. Do not submit the full list again just because a browser or API connection timed out.

1. Wait for the original run to finish. A running or stopping run can still produce results.
2. Download its complete default dataset as JSON, without selected-field, offset or result-limit filters. Keep this export with the original input; a filtered export can look like an incomplete run.
3. Keep rows with `error: null` and an HTTP status from 200 to 399. An empty `technologyNames` array is not itself a failed website; it means no confident technology was found.
4. Review actual error rows. A temporary connection failure or HTTP 503 may be worth retrying later. HTTP 401, 403, 429, bot checks, invalid domains and certificate errors need manual review, not a blind retry or an attempt to bypass a restriction.
5. If a requested URL has no exported row, inspect the original run before resubmitting it. A run limit, spending cap, abort or incomplete export may explain the gap; absence is not proof that the URL was never processed.
6. When you decide to retry, submit only the reviewed failed URLs, preserving your category and script-scan choices. Check the new run's own charge cap and run options. There is no cross-run billing deduplication: successful sites submitted again can be charged again, and a retry run that reads a website incurs its own start charge.

Use the exact input URL as the reconciliation key, not just the domain or the redirected `finalUrl`. Two different pages on one domain can reveal different technologies. Keep successful rows and their `analysisWarnings` in your saved inventory rather than discarding the whole batch.

### Related tools for a website audit

After comparing technology stacks, use these separate tools when you need domain or page-level checks:

- [Domain Intelligence: WHOIS, DNS & SSL Checker](https://apify.com/datalantern/domain-intel) checks domain registration, DNS records and TLS certificate details.
- [Website Health Audit: Broken Links, SEO & Speed](https://apify.com/datalantern/website-health-audit) checks broken links, HTTP status and on-page SEO.

Each Actor runs separately under its own listed pricing. Following a link does not start a run or incur an Actor event charge.

# Actor input Schema

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

One website per line, for example apify.com or https://www.shopify.com. The home page (or the exact page you enter) is analysed.

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

Stop after this many analysed websites. 0 means no limit.

## `includeCategories` (type: `array`):

Optional. Leave empty to get every technology. The summary columns (cms, ecommerce, analytics, …) are always filled.

## `scanScripts` (type: `boolean`):

Also scan the website's own JavaScript files (up to 20 per site). Finds technologies that only appear in app bundles, such as payment providers in single-page apps. Turn off for faster runs.

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

Usually not needed. Turn on if some websites block the request (HTTP 403).

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://www.shopify.com",
    "https://wordpress.org"
  ],
  "maxResults": 100,
  "includeCategories": [],
  "scanScripts": true,
  "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://apify.com",
        "https://www.shopify.com",
        "https://wordpress.org"
    ]
};

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

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

```

## MCP server setup

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