# Tech Stack Detector — CMS, Framework & Analytics (`keyman98/tech-stack-detector`) Actor

Detect a website's CMS, JS framework, analytics, CDN, server and e-commerce platform from 174 self-authored signatures. Fast HTTP-only fingerprinting, no browser, no third-party data.

- **URL**: https://apify.com/keyman98/tech-stack-detector.md
- **Developed by:** [KeyMan98](https://apify.com/keyman98) (community)
- **Categories:** SEO tools, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 site analyzeds

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

## Tech Stack Detector

Find out what a website is built with: CMS, JavaScript framework, analytics tools, CDN, server software, e-commerce platform, and more — using only plain HTTP requests (no browser), so it stays fast and cheap even on long lists of sites.

### What it does

For each URL, this Actor sends a normal HTTP GET request (the same request any browser would send) and inspects the response: HTTP headers, cookies set, `<meta>` tags, and script tags. It compares what it finds against a built-in database of 170+ technology signatures and reports every match, with a confidence score and — when detectable — a version number.

### How detection works

Detection is entirely signature-based, using **self-authored** rules (not Wappalyzer's or any other third-party fingerprint database, nor its data): every rule in this Actor encodes a publicly documented, independently observable fact about how a given technology exposes itself over plain HTTP — a default response header, a `<meta name="generator">` tag, a static-asset path pattern, a cookie name it sets, or a snippet of its own loader script URL. No proprietary fingerprint dataset is bundled or reused.

### What you get (output fields)

One dataset row per URL:

- `url` — the URL that was requested.
- `finalUrl` — URL after following redirects (`null` on error).
- `technologies` — array of `{ name, category, version, confidence }` for every match. `version` is `null` when it could not be determined. `confidence` is 0-100.
- `error` — set only if the site could not be reached at all; `null` on success.

Categories used: `cms`, `ecommerce`, `javascript-framework`, `javascript-library`, `analytics`, `tag-manager`, `cdn`, `server`, `web-framework`, `hosting`, `chat`, `marketing-automation`, `forms`, `video`, `reviews`, `maps`, `payment`, `fonts`, `security`, `search`.

### Who it's for

- **Sales/lead qualification** — see what a prospect's site already runs (e.g. "still on WordPress", "already using Shopify") before a call.
- **Competitive research** — compare the tech stack across a list of competitor sites.
- **Agencies/consultants** — audit a client site's current stack before a migration or redesign.

### How to use

1. **URLs** — paste the sites you want analyzed (a bare domain like `example.com` also works).
2. **Include categories (optional)** — restrict the report to specific categories (e.g. `["cms", "analytics"]`). Leave empty to report everything detected.
3. **Run the Actor.** Each site becomes one dataset row.

### Input example (JSON)

```json
{
  "urls": ["https://example.com", "https://apify.com"],
  "includeCategories": []
}
```

### Output example (JSON)

```json
{
  "url": "https://apify.com",
  "finalUrl": "https://apify.com",
  "technologies": [
    { "name": "Amazon CloudFront", "category": "cdn", "version": null, "confidence": 95 },
    { "name": "Google Tag Manager", "category": "tag-manager", "version": null, "confidence": 95 },
    { "name": "HubSpot", "category": "marketing-automation", "version": null, "confidence": 90 },
    { "name": "Intercom", "category": "chat", "version": null, "confidence": 90 }
  ],
  "error": null
}
```

### If a site cannot be reached

A site that is completely unreachable (DNS failure, connection refused, timeout) is a row with `error` set: the rest of the list keeps running, the run does not fail, and you are **not charged** for that URL.

A site that responds with an HTTP error status (404, 500, ...) is **not** treated as an error: the server did respond, and its headers and HTML still reveal its technology stack, so that row is analyzed normally (and charged, like any other successful analysis) — its `technologies` array may simply be shorter.

### Pricing

Pay only for sites actually reached and analyzed — nothing charged for sites that could not be reached at all. Pricing model: **pay-per-event**.

| Event | When it's charged | Price |
| --- | --- | --- |
| `site-analyzed` | a site was reached and analyzed, regardless of how many technologies were found | 0.003 USD |

### Limitations

- HTTP-only: it does not run a browser, so it will not detect technologies that only reveal themselves after JavaScript execution (e.g. content injected client-side with no trace in the initial HTML or scripts).
- Detects what the page's first HTML response, headers and cookies expose — not what is running behind an authenticated area.
- Version numbers are reported only when a signature's pattern captures one; many technologies do not expose a version over plain HTTP and are reported with `version: null`.
- Signature coverage is broad (170+ technologies) but not exhaustive; niche or in-house-built tools will not be recognized.
- No login, no CAPTCHA solving, no bypass of any site protection.

### FAQ

#### Does this use a headless browser like Playwright/Puppeteer?

No, by design — it uses plain HTTP requests only, which keeps runs fast and cheap. This means JavaScript-only signals (nothing visible in the raw HTML/headers/cookies) are not detected.

#### Why is a site's stack incomplete or empty?

Either the technology is not in the 170+ signature database, or it only reveals itself via client-side JavaScript execution, which this Actor does not run.

#### Am I charged if a site is unreachable?

No. You are only charged for sites that were actually reached and analyzed — including sites that return an HTTP error page, since that page was still fetched and analyzed.

#### Can I filter to only certain categories?

Yes, use the `includeCategories` input field (e.g. `["cms", "ecommerce"]`).

#### Is this based on Wappalyzer?

No. The signature database is self-authored for this Actor; see "How detection works" above.

#### Is this a Wappalyzer or BuiltWith replacement?

Partial. It covers the main categories (CMS, JS frameworks, analytics, CDN, server, e-commerce, and more) with its own 170+ signatures, but it isn't a drop-in superset of a database with thousands of entries. If your site uses something niche or in-house-built, it may not be recognized.

#### Why are your signatures self-authored instead of using Wappalyzer's database?

No third-party fingerprint dataset examined had a license clearly compatible with reuse here, so every rule was written from scratch against publicly documented, independently observable facts (an HTTP header, a `<meta name="generator">` tag, a cookie name, a script URL pattern) — see "How detection works" above.

#### Can I use this through the Apify API or an MCP server?

Yes. Like any Apify Actor, you can run it and read results through the standard Apify API, or through the Apify MCP server if you use Claude, Cursor, or another MCP-enabled client.

### Export

Results can be downloaded from the Apify dataset as JSON, CSV, or Excel, or accessed via the Apify API.

# Actor input Schema

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

Public URLs (or bare domains) to analyze.

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

Only report technologies in these categories (e.g. "cms", "javascript-framework", "analytics", "cdn", "server", "ecommerce"). Leave empty to report all categories.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "https://apify.com"
  ],
  "includeCategories": []
}
```

# Actor output Schema

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

All results in the default dataset (JSON, CSV, Excel).

# 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://example.com",
        "https://apify.com"
    ],
    "includeCategories": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("keyman98/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://example.com",
        "https://apify.com",
    ],
    "includeCategories": [],
}

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

```

## MCP server setup

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