# Website Tech Stack: Reverse Search & Changes (HTTP Archive) (`pracadata/website-tech-stack`) Actor

Which websites use a technology (reverse search by popularity), what technologies a site added or removed month to month, and the full stack of any domain in the crawl. Real-browser detection by the HTTP Archive on desktop and mobile, no live crawling, no contact data. API and AI-agent (MCP) ready.

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

## Pricing

from $5.00 / 1,000 website using a technologies

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 Reverse Search & Change History (16M sites, monthly)

### What does it do?

Answers the questions a live "what does this site run?" tool cannot:

- **`reverse`**: *which websites use a technology or category?* "Every site in the top 100k that runs Klaviyo", "all Shopify stores in the top 1M", "the e-commerce platforms of the top 10k". Filtered by popularity, one row per website.
- **`changes`**: *who switched?* "Sites that **added** Shopify since June", "sites that **removed** Magento", "WordPress 6.5 → 6.6". Month-to-month changes for **16 million websites**, with a flag for whether desktop and mobile agree.

And, for completeness:

- **`lookup`**: the full stack of the domains you list (up to 10,000 per run), from the same monthly snapshot.
- **`adoption`**: how many sites use a technology, by popularity bucket and month ("share of Next.js in the top 10k").

The data is the **[HTTP Archive](https://httparchive.org) monthly crawl**: every website in the Chrome UX Report (**16.1 million origins**, 12.5 million registrable domains) loaded in a **real browser, on desktop and mobile**, with technologies detected by the HTTP Archive's open-source fork of Wappalyzer (3,600+ technologies in 100+ categories, with the version when the page exposes it). Use it as an API, schedule it, or call it from an AI agent through MCP (see "Use with AI agents (MCP)").

**Data last updated: 27 October 2026** (latest crawl: **October 2026**; `RUN_SUMMARY.latest_crawl` and `data_build_date` on every run). Refreshed monthly, after the HTTP Archive crawl is final.

**Try before you pay:** the adoption figures, the technology catalog and a slice of the month-to-month changes (top 10k, latest month) are a free dataset on Hugging Face: <https://huggingface.co/datasets/pracadata/website-technologies>. Check the schema and the quality there, then come back for the reverse search, the full change history and the API.

### How is this different from a lookup tool?

| | Tools that detect a site live | This Actor |
|---|---|---|
| Question it answers | What does this URL run **right now**? | Which sites run X? What changed? What did this domain run in the last crawl? |
| Coverage | Any URL you give | The **16.1 million popular origins** of the Chrome UX Report. Small and brand-new sites are not in it |
| Freshness | Today | **Monthly**: data is up to about six weeks old |
| History | Usually none | Change feed since the first crawl of this build, and 3 crawls of lookup |
| Reliability | Timeouts and blocked requests happen | Served from a prepared snapshot: **no live crawling, no timeouts, no proxies**, same answer every time |
| Detection | Mostly the HTML of one request | **Real browser**, so JavaScript-only technologies (tag managers, SPA frameworks, analytics) are found |
| Contact data | Some add e-mails and phones | **None**: websites and software only |

If you need to check a site that is not in the 16.1M, or must see today's stack, use a live tool. If you need lists ("who uses X"), movements ("who switched to X") or trends, this is the data.

### What data can it return?

| Mode | One row is | Main fields |
|---|---|---|
| `reverse` | one website using the technology or category | `origin`, `reg_domain`, `rank_bucket`, `crawl_month`, `technology`, `categories`, `versions`, `detected_desktop`, `detected_mobile`, `source` |
| `changes` | one change of one website between two crawls | `month_from`, `month_to`, `origin`, `reg_domain`, `rank_bucket`, `technology`, `change_type`, `old_versions`, `new_versions`, `confidence`, `source` |
| `lookup` | one domain you asked for | `input`, `reg_domain`, `origin_urls`, `n_origins`, `crawl_month`, `rank_bucket`, `technologies` (list of `{technology, categories, versions, detected_desktop, detected_mobile}`), `n_technologies`, `source` |
| `adoption` | one technology × category × month | `crawl_month`, `technology`, `category`, `rank_bucket`, `origins`, `origins_in_bucket`, `share_of_origins`, `source` |

`rank_bucket` is the Chrome UX Report popularity bucket: `1000` means the top 1k websites, then 5k, 10k, 50k, 100k, 500k, 1M, 5M, 10M and 50M. `source` carries the attribution on every row: **Derived from the HTTP Archive (October 2026 crawl)**.

### How to use it

1. Choose a **mode**.
2. `reverse`: type technology names (`Shopify`, `Klaviyo`, `Google Tag Manager`) or categories (`Ecommerce`, `CMS`). `changes`: technologies and/or domains, plus `since_month`. `lookup`: paste domains or URLs. `adoption`: technologies or categories.
3. Optional: `rank_max` (top N), `min_confidence` (`both_clients` = desktop and mobile agree), `change_types`, `month`.
4. Set **Maximum results** (default 1,000). Results are charged one by one, so this is your cost cap.
5. Run it and download JSON, CSV or Excel, or read the dataset through the API.

#### Input examples

Websites in the top 100k that use Klaviyo, latest crawl:

```json
{ "mode": "reverse", "technologies": ["Klaviyo"], "rank_max": 100000, "max_rows": 5000 }
```

E-commerce platforms of the top 10k, both clients agreeing:

```json
{ "mode": "reverse", "categories": ["Ecommerce"], "rank_max": 10000, "min_confidence": "both_clients", "max_rows": 10000 }
```

Who added Shopify since the oldest month, in the top 1M (changes both desktop and mobile agree on, the default):

```json
{ "mode": "changes", "technologies": ["Shopify"], "since_month": "2026-08", "rank_max": 1000000, "change_types": ["added"], "max_rows": 5000 }
```

Who removed Shopify (not detected in the later crawl) in the same period:

```json
{ "mode": "changes", "technologies": ["Shopify"], "since_month": "2026-08", "rank_max": 1000000, "change_types": ["removed"], "max_rows": 5000 }
```

Monthly share of Next.js in the top 10k:

```json
{ "mode": "adoption", "technologies": ["Next.js"], "rank_max": 10000 }
```

Full stack of three domains (the default input, a fast and cheap test):

```json
{ "mode": "lookup", "domains": ["shopify.com", "wordpress.org", "nytimes.com"] }
```

#### Output example (lookup)

```json
{ "input": "www.example.com", "reg_domain": "example.com", "origin_urls": ["https://www.example.com/"], "n_origins": 1, "crawl_month": "2026-10", "rank_bucket": 100000,
  "technologies": [
    { "technology": "WordPress", "categories": ["CMS", "Blogs"], "versions": ["6.6.2"], "detected_desktop": true, "detected_mobile": true },
    { "technology": "Cloudflare", "categories": ["CDN"], "versions": [], "detected_desktop": true, "detected_mobile": true }
  ],
  "n_technologies": 2, "source": "Derived from the HTTP Archive (October 2026 crawl)" }
```

### Read this before you trust a change

A change is a difference between **two monthly crawls**, and detections flicker (A/B tests, consent banners, lazy loading, a script that loads only sometimes). We measured how often a change reverses in the following month, over four consecutive month triples:

| Changes | Reverse the next month |
|---|---|
| Desktop **and** mobile agree (`both_clients`) | **4.3%** overall; 9% of `added` and 12% of `removed` |
| Only one client saw it (`single_client`) | **15.6%** overall; 26% of `added` and 27% of `removed` |

So `changes` returns **`both_clients` by default** (set `min_confidence` to `any` to include single-client changes; `version_changed` is reliable even single-client, 3.7%). **`removed` means "not detected in this crawl", not "the site uninstalled it"**; it is a strong signal for sales triggers, not a fact. The noise is higher among the most popular sites (many conditional scripts and bot-management layers). A website that is **missing from a month's crawl** never appears as `removed`: such sites are the separate `entered_crawl` / `left_crawl` change types. Half of all changes are version changes (Facebook Pixel, WordPress and a handful of other technologies produce almost half of them): filter by `change_types` and `technologies`.

### Use with AI agents (MCP)

The Apify MCP server lets an AI agent find this Actor, read its input schema and call it as a tool (`call-actor`; the run's `maxTotalChargeUsd` goes in `callOptions`) and read the rows (`get-dataset-items`).

```
https://mcp.apify.com?tools=pracadata/website-tech-stack
```

- **Claude (Desktop or web):** Settings → Connectors → Add custom connector, paste the URL above, sign in to Apify.
- **Cursor, VS Code and other `mcpServers` clients:** `{ "mcpServers": { "apify": { "url": "https://mcp.apify.com?tools=pracadata/website-tech-stack" } } }`. Without a browser, add `"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }`.

**What an agent should know.** "Which sites use X?" → `reverse` with `rank_max` and a small `max_rows`. "Did X switch platforms?" → `changes` with `domains` (leave the three pre-filled example domains out, or replace them). "What does site X run?" → `lookup`. "How popular is X?" → `adoption` (small and cheap). Always set `max_rows` and a maximum charge per run; read `RUN_SUMMARY.latest_crawl` for freshness and `RUN_SUMMARY.not_found` for domains outside the snapshot.

| Question | Input | Rows | Cost (at the prices below) |
|---|---|---|---|
| "Which top-100k sites use Klaviyo?" | `reverse`; Klaviyo, `rank_max` 100000 | about 1,000 | US$ 0.005 per site |
| "Which top-1M stores added Shopify since August?" | `changes`; Shopify, `added`, `since_month` 2026-08 | depends | US$ 0.004 per change |
| "What tech stack does example.com use?" | `lookup`; `domains` \[example.com] | 1 | US$ 0.0015 |
| "How has Next.js adoption moved in the top 10k?" | `adoption`; Next.js, `rank_max` 10000 | about 3 | US$ 0.002 per row |

### Pricing

This Actor uses **pay per event**. The Pricing tab is authoritative; at the time of writing:

| Event | What you are charged for | Price |
|---|---|---|
| `reverse-lookup-row` | each website returned by `reverse` | US$ 0.005 |
| `change-row` | each change returned by `changes` | US$ 0.004 |
| `domain-lookup` | each domain found by `lookup` | US$ 0.0015 |
| `adoption-row` | each row returned by `adoption` | US$ 0.002 |

Domains not in the snapshot are free. Results are charged before they are delivered, in batches, and the Actor stops when your *maximum charge per run* is reached: you never get a row you were not charged for, and you are never charged for a row you did not get (`RUN_SUMMARY.charged`, `stopped_by_spending_limit`). There is no start fee.

### Coverage and limitations

- **Popular websites only.** The list is the Chrome UX Report: 16.1 million origins with enough Chrome traffic. Small and brand-new websites are not in it. In `lookup`, a domain that is not in the crawl, or has no technology detected, is **free** and listed in `RUN_SUMMARY.not_found`.
- **Home page only.** Technologies used only on inner pages (checkout, blog, login) can be missed.
- **About 3,600 technologies**, far fewer than commercial databases. No detection does not prove that a site does not use a technology.
- **Monthly, with lag.** The crawl starts on the second Tuesday of the month and takes 1 to 2 weeks; the snapshot is updated after it is final. `lookup` keeps the 3 latest crawls, `reverse` the latest, `changes` and `adoption` the history since August 2026.
- **`reverse` above the top 1M and `lookup`** read from a private store, so the first row takes a couple of seconds. `reverse` inside the top 1M is served from the Actor itself. Above the top 1M a run reads at most 25 technologies (a whole category is fine at `rank_max` 1,000,000 or lower).
- **Shared platforms.** `name.blogspot.com` and `name.github.io` belong to their platform's registrable domain (`blogspot.com`, `github.io`). Use `reverse`/`changes` rows, which carry the full `origin`, to tell them apart.
- **Detections flicker** between months (see above). **Versions are best effort**, taken from what the page exposes; some are wrong.
- **Crawled from the US.** Websites that show different content by country may differ.

### Privacy

The data describes **websites and the software they run**. Each row has a website's root URL, registrable domain, popularity bucket and detected technologies; no page content, no inner URLs, no cookies, no WHOIS, no e-mails, no phone numbers, no names. Do not use it to identify or contact individuals. An origin can be a personal website: removal requests go to `lucashpissaia@gmail.com` and take effect in the next monthly build.

### Source and licence

- **Derived from the HTTP Archive** (<https://httparchive.org>) monthly crawls, public BigQuery dataset `httparchive.crawl.pages`. **Every row carries this attribution in `source`.**
- **Detection** is done by the HTTP Archive's open-source fork of Wappalyzer. Only detection results are served; detection rules, icons and descriptions are **not** redistributed. Technology names are trademarks of their owners and are used only to identify them.
- **Popularity buckets** come from the Chrome UX Report (Google), as included by the HTTP Archive.
- **Licence: none published by the source.** The HTTP Archive publishes no formal licence for its data. Its maintainers have said in public that "people are free to use the data however they want" and that attribution is appreciated ([discussion](https://discuss.httparchive.org/t/what-are-the-rights-licensing-for-data-in-http-archive/1924)). We do not claim a licence for this product. We have asked the HTTP Archive for written confirmation and have not received an answer yet; if it asks us to stop, the Actor is withdrawn.
- **Not affiliated with or endorsed by** the HTTP Archive, Google or Wappalyzer.
- **Removal requests:** write to `lucashpissaia@gmail.com` with the origin or domain. It goes into the opt-out list that every build applies before anything is published.
- **Use:** your own analysis, prospecting lists, models and products built on the results. Do not resell the raw rows in volume or as a substantially similar dataset or API.
- **No warranty:** provided as is, with the limitations above.

### FAQ and support

**How fresh is the data?** Monthly. `RUN_SUMMARY.latest_crawl` is the crawl month and `data_build_date` the build date.

**Can I check a site that is not in the list?** Not in this version: there is no live detection. The domain is listed in `not_found` and not charged.

**Why does `changes` return fewer rows than I expected?** The default is `min_confidence: both_clients`, which drops the changes only one of desktop and mobile saw (42% of them), because those reverse far more often. Set `any` to include them.

Found a problem or need another field? Use the Issues tab.

# Actor input Schema

## `mode` (type: `string`):

What to return. `reverse` needs `technologies` or `categories`. `changes` takes `domains` and/or `technologies`. `lookup` needs `domains`. `adoption` takes `technologies` and/or `categories`. Default: lookup (three example domains).

## `domains` (type: `array`):

`lookup` and `changes`. Domains or URLs (`example.com`, `www.example.com`, `https://shop.example.co.uk/page` all work). They are matched by registrable domain: `lookup` returns one row per domain, merging every origin of that domain in the crawl (`www.`, `shop.`...; listed in `origin_urls`), charged once. Sites on a shared platform (`name.blogspot.com`, `name.github.io`) belong to the platform's domain. Up to 10,000 per run. A domain not in the snapshot, or with no technology detected, is not charged and is listed in `RUN_SUMMARY.not_found`. In `changes` the three example domains that come pre-filled are ignored: list your own.

## `technologies` (type: `array`):

`reverse`, `changes` and `adoption`. Technology names as the HTTP Archive detects them, case-insensitive (`Shopify`, `WordPress`, `Klaviyo`, `Google Tag Manager`, `Cloudflare`). An unknown name is refused with the closest matches. Up to 20.

## `categories` (type: `array`):

`reverse` and `adoption`. Technology categories, case-insensitive (`Ecommerce`, `CMS`, `Analytics`, `Tag managers`, `CDN`, `JavaScript frameworks`, `Payment processors`). Combined with `technologies` as OR. Up to 10. In `reverse` with `rank_max` above 1,000,000 a run reads at most 25 technologies (each one is read from external storage).

## `rank_max` (type: `integer`):

Popularity: only websites in the top N by Chrome UX Report rank. Buckets: 1,000; 5,000; 10,000; 50,000; 100,000; 500,000; 1M; 5M; 10M; 50M (a value in between is rounded down to a bucket). `reverse` and `changes`: every website up to that bucket. `adoption`: the cumulative figure for exactly that bucket (top N). Default: all (50M).

## `min_confidence` (type: `string`):

`both_clients`: only technologies detected by both the desktop and the mobile crawl (and, in `changes`, only changes both clients agree on). `any`: either client. Default: `both_clients` in `changes` (single-client changes reverse in the next month 15.6% of the time, both-client ones 4.3%) and `any` in `lookup` and `reverse`.

## `month` (type: `string`):

`lookup`, `reverse` and `adoption`: crawl month, YYYY-MM. Default: the latest crawl (`RUN_SUMMARY.latest_crawl`). `lookup` keeps the 3 most recent crawls; `reverse` only the latest; `adoption` has the full history (all months when empty).

## `since_month` (type: `string`):

`changes` only: first month to return (the `month_to` of the change), YYYY-MM. Default: the latest month only.

## `change_types` (type: `array`):

`changes` only. Default: `added` and `removed`. `removed` means "not detected in this crawl", not "uninstalled". `entered_crawl`/`left_crawl` say a website appeared in or disappeared from the crawl, which is not the same as adding or removing a technology (they cannot be combined with `technologies`).

## `include_versions` (type: `boolean`):

Add the detected versions (best effort, from what the page exposes) to each technology or change. Does not change the price.

## `max_rows` (type: `integer`):

Maximum number of results (rows) returned and charged. Default 1,000. Set a small value (for example 10) to test a query. If more rows match, `RUN_SUMMARY.capped_by_max_rows` is true.

## Actor input object example

```json
{
  "mode": "lookup",
  "domains": [
    "shopify.com",
    "example.co.uk"
  ],
  "technologies": [
    "Klaviyo"
  ],
  "categories": [
    "Ecommerce"
  ],
  "rank_max": 100000,
  "month": "2026-09",
  "since_month": "2026-06",
  "change_types": [
    "added",
    "removed"
  ],
  "include_versions": true,
  "max_rows": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with the returned rows. The view that applies depends on the input field `mode`

## `files` (type: `string`):

Key-value store with RUN_SUMMARY

## `summary` (type: `string`):

RUN_SUMMARY: mode, applied filters, rows returned and charged, domains not found, latest crawl, data build date, and whether the maximum charge stopped the run

# 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 = {
    "domains": [
        "shopify.com",
        "wordpress.org",
        "nytimes.com"
    ],
    "max_rows": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("pracadata/website-tech-stack").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 = {
    "domains": [
        "shopify.com",
        "wordpress.org",
        "nytimes.com",
    ],
    "max_rows": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("pracadata/website-tech-stack").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 '{
  "domains": [
    "shopify.com",
    "wordpress.org",
    "nytimes.com"
  ],
  "max_rows": 10
}' |
apify call pracadata/website-tech-stack --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pracadata/website-tech-stack"
        }
    }
}
```

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/yJo1JTSd56kNSa6dx/builds/RDkhmtQ7ze4EfaDKv/openapi.json
