# Tech Stack Detector, Website Technology Lookup, Wappalyzer (`thoob/website-tech-stack-detector`) Actor

Input website URLs, get the technologies each one runs (CMS, server, frameworks, analytics, payments), detected from its own served HTML, headers, and cookies. Each detection carries its evidence and a confidence. Billed only per URL with at least one detection, no start fee.

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

## Pricing

$2.00 / 1,000 url 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

## Website Tech Stack Detector

Give it a list of website URLs and get back the technologies each site runs:
CMS, web server, frameworks, JavaScript libraries, analytics, tag managers,
payments, CDN, and hosting. Every detection comes with the exact on-page signal
that proved it and a confidence score.

The rule is simple: no guessing. A technology is reported only when an explicit
signal in the site's own served page proves it. If nothing on the page proves a
technology, it is not in the results. You are never handed a guess to act on.

### What proves a detection

The actor reads each page's own served HTML, HTTP response headers, and cookies,
then fingerprints them against a curated, high-precision ruleset. Each detection
records which signal matched:

- response headers (for example `Server: nginx`, `X-Powered-By: PHP/8.2`,
  `X-Generator: Drupal`, `CF-Ray` for Cloudflare, `X-Shopify-Stage` for Shopify)
- Set-Cookie names (for example `wordpress_logged_in_*`, `_shopify_*`,
  `laravel_session`, `ASP.NET_SessionId`)
- the `<meta name="generator">` tag (WordPress, Drupal, Joomla, Wix, Squarespace,
  Ghost, Hugo, and more, with the version when the tag carries one)
- script and link URLs (for example `/wp-content/` for WordPress,
  `cdn.shopify.com` for Shopify, `/_next/static` for Next.js, `gtag/js` for
  Google Analytics) and a few unambiguous HTML markers (for example
  `ng-version` for Angular)

This is a curated high-precision starter set covering the common, high-signal
technologies. It is not an exhaustive fingerprint database, and it favors
precision over coverage: a rule that would misfire on a real site is tightened or
left out. A version is reported only when it was captured verbatim from the
matched signal. Versions are never invented.

### Input

- **URLs**: one per line.
- **Respect robots.txt**: when on (default), the host's robots.txt is checked and
  disallowed URLs are skipped.
- **Delay between requests to one host**: a polite throttle (default 1 second) so
  pages are never fetched back to back.
- **Max delivered URLs**: cap on billed rows (0 = no cap).

### Output

One row per analyzed URL: `url`, `finalUrl`, `httpStatus`, `server`, `techCount`,
`categories`, and a `detections` array. Each entry in `detections` carries
`tech`, `category`, `evidence` (the literal signal), `confidence`, and `source`
(which served artifact carried the signal), plus row provenance (`sourceUrl`,
`retrievedAt`, `confidence`, `dataSource`).

The row `confidence` is the strongest detection confidence on the page. Each
detection carries its own. Confidence is 1.0 for an explicit version or an
unambiguous vendor string, 0.8 for an unambiguous marker without a version, and
0.6 for a strong heuristic marker that several look-alikes could share.

### How it works, and how it stays polite

The actor fetches each page once, with a declared identifying User-Agent. It
reads robots.txt first and skips anything disallowed for our agent, keeps a
jittered delay between requests to the same host, and treats a refusal
(HTTP 401, 403, 429, or 451) as the host declining: it stops requesting that host
for the rest of the run rather than retrying. A URL that is blocked, disallowed by
robots, fails to fetch, or yields no evidenced detection is written to a separate
free dataset and is not billed. A site owner can ask us to skip their domain at
https://ponodata.com/opt-out ; opted-out hosts are skipped and never charged.

### Billing

Pay only per URL that returns at least one evidenced detection. There is no
per-run start fee. URLs that are blocked, disallowed, fail, or return no detection
cost nothing.

### Sample output

A real run reading each site's own served HTML, headers, and cookies:

| URL | Techs | Categories | Server |
| --- | --- | --- | --- |
| https://wordpress.org | 5 | CMS, Web server, Font / icons | nginx |
| https://www.shopify.com | 4 | CDN / proxy, Ecommerce, JavaScript framework | cloudflare |
| https://vercel.com | 3 | Hosting / PaaS, Web framework | Vercel |
| https://getbootstrap.com | 5 | CDN / proxy, Hosting / PaaS, UI framework | cloudflare |

For wordpress.org, one detection reads `{tech: "WordPress 7.1", category: "CMS",
evidence: "meta generator: WordPress 7.1-alpha", confidence: 1.0}`. Open the
`sourceUrl` and view source to verify every signal. Sites that return no
evidenced technology route to the free reject dataset.

### See also

More clean, pay-only-for-results data tools from Pono Data:

- [URL Metadata & OpenGraph Extractor](https://apify.com/thoob/url-metadata-extractor) - page head tags for link previews
- [Bulk DNS Lookup](https://apify.com/thoob/dns-bulk-lookup) - DNS records plus SPF, DMARC, and CAA
- [Domain WHOIS via RDAP](https://apify.com/thoob/rdap-domain-lookup) - registration data, structured from RDAP

Full catalog: https://apify.com/thoob

# Actor input Schema

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

Website URLs to analyze, one per line. The actor fetches each page once and fingerprints its own served HTML, response headers, and cookies against a curated high-precision ruleset.

## `respectRobots` (type: `boolean`):

Check the host's robots.txt before fetching and skip any URL it disallows for our agent. Recommended on.

## `requestDelaySecs` (type: `integer`):

Polite throttle. A jittered wait of at least this many seconds is kept between requests to the same host, so pages are never fetched back to back. 0 disables the wait.

## `maxUrls` (type: `integer`):

Cap on delivered, billed rows. 0 means no cap. The platform spend cap is honored regardless.

## Actor input object example

```json
{
  "urls": [
    "https://wordpress.org",
    "https://www.shopify.com"
  ],
  "respectRobots": true,
  "requestDelaySecs": 1,
  "maxUrls": 0
}
```

# Actor output Schema

## `detections` (type: `string`):

One row per analyzed URL, each with its evidenced detections.

# 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://www.shopify.com"
    ]
};

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

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

```

## MCP server setup

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