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

Detect the technology stack of any website: CMS, frameworks, JavaScript libraries, analytics, web servers and CDN - with versions where detectable and the evidence for every match. 7,500+ fingerprints (community Wappalyzer DB), one GET per URL, no crawling. No credentials needed.

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

## Pricing

from $3.00 / 1,000 results

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

## Tech Stack Detector — CMS, Frameworks & Analytics

Detect what any website is built with: CMS, frameworks, JavaScript libraries,
analytics, web servers, CDN and hosting — from **7,500+ community-maintained
fingerprints**, with **versions where detectable** and the **evidence for
every match** (which header, cookie, meta tag or script gave it away).
One GET per URL, no crawling, no credentials.

### What it does

Give it a list of website URLs (bare domains work — `https://` is assumed).
For each site you get one dataset item with:

- `technologies[]` — every detected technology:
  - `name` and `categories` (CMS, JavaScript libraries, Analytics, …)
  - `version` when the fingerprint can extract one (e.g. `jQuery 3.6.0`, `WordPress 6.4`)
  - `evidence[]` — exactly what matched: `headers:server`, `cookies:phpsessid`,
    `meta:generator`, `scriptSrc`, `html`, `url`, or `implied by WordPress`
- `byCategory` — `{"CMS": ["WordPress"], "Web servers": ["Nginx"]}` summary
- `technologyCount`, `finalUrl`, `httpStatus`, `checkedAt`

Implied technologies are expanded transitively (WordPress ⇒ PHP ⇒ MySQL).
Failed pages (404, non-HTML, timeouts) produce a failed item with a clear
error — batch jobs always finish.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array | — | Websites to analyze (only these are fetched; no crawling) |
| `timeoutSecs` | integer | `20` | Download timeout per page |
| `maxFileSizeMb` | integer | `5` | Larger pages are skipped with a failed item |

### Example output (abridged)

```json
{
    "url": "https://github.blog",
    "status": "success",
    "technologies": [
        {"name": "Nginx", "categories": ["Web servers", "Reverse proxies"], "version": null, "evidence": ["headers:server"]},
        {"name": "WordPress", "categories": ["CMS", "Blogs"], "version": "7.0.4", "evidence": ["meta:generator", "html"]},
        {"name": "MySQL", "categories": ["Databases"], "version": null, "evidence": ["implied by WordPress"]},
        {"name": "Yoast SEO", "categories": ["SEO"], "version": "28.2", "evidence": ["html"]}
    ],
    "technologyCount": 13,
    "byCategory": {"CMS": ["WordPress"], "Databases": ["MySQL"], "Web servers": ["Nginx"]},
    "checkedAt": "2026-08-18T09:30:00Z"
}
```

### Typical uses

- Sales/market research: segment prospect lists by CMS, e-commerce platform or analytics stack
- Competitive analysis: what are competitors built on, did they migrate
- Security/IT inventory: find sites still running outdated library versions
- Enrich domain datasets with technology columns

### Fingerprint database: license & updates

- The detection rules are a compact compilation of
  **[enthec/webappanalyzer](https://github.com/enthec/webappanalyzer)** — the
  community-maintained continuation of the Wappalyzer technology database —
  used under its **GPL-3.0** license. The compiled snapshot
  (`src/data/fingerprints.json`) keeps the license notice plus provenance
  metadata (source repo, upstream commit hash, retrieval date) in its `_meta`
  block, and remains under GPL-3.0. The detection engine reads it as data.
- **Updating**: run `python scripts/update_fingerprints.py` (fetches the
  latest upstream DB, recompiles the snapshot, prints the new commit hash),
  then rebuild the Actor. We refresh the snapshot periodically.

### Limitations

- Static analysis of the served HTML: technologies only visible after
  client-side JavaScript execution may be missed (`js`/`dom` fingerprints are
  out of scope by design)
- Sites behind aggressive bot protection may fail or reveal only their CDN
- Version extraction depends on the site exposing it (headers, generator tags,
  asset paths) — `version` is null when it doesn't

### Development (local)

```bash
cd actors/tech-stack-detector
uv venv --python 3.13 .venv && uv pip install -p .venv/bin/python -r requirements.txt

.venv/bin/python tests/run_local_test.py           # end-to-end test (apify run equivalent), exit 0 = ALL PASS
../../node_modules/.bin/apify run                  # real apify CLI local run (input: storage/key_value_stores/default/INPUT.json)
```

The test runs a mock WordPress-behind-Nginx site against the real bundled DB
(headers/cookies/meta/scriptSrc/html matching, version extraction, implies
expansion read from the DB itself); expected results live in
`tests/expected_output.json`. Publishing → `../../docs/publishing.md`.

# Actor input Schema

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

Websites to analyze (one per line; bare domains get https://). Only these exact URLs are fetched - no crawling.

## `timeoutSecs` (type: `integer`):

Timeout for downloading each page.

## `maxFileSizeMb` (type: `integer`):

Pages larger than this are skipped with a failed item.

## Actor input object example

```json
{
  "urls": [
    "https://wordpress.org",
    "https://apify.com"
  ],
  "timeoutSecs": 20,
  "maxFileSizeMb": 5
}
```

# Actor output Schema

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

All dataset items (one per URL): technologies\[], versions, evidence, byCategory.

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

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

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

```

## MCP server setup

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