# Website Tech Stack Scraper - Detect Technologies in Bulk (`martin10291/website-tech-stack-detector`) Actor

Find out which technologies a website runs on, for hundreds of company domains in one run. For every domain you get the detected technologies with version, confidence, category and website (CMS, e-commerce platform, analytics, tag managers, CDN, hosting, JavaScript frameworks, payment, marketing too

- **URL**: https://apify.com/martin10291/website-tech-stack-detector.md
- **Developed by:** [Martin Petrov](https://apify.com/martin10291) (community)
- **Categories:** Lead generation, SEO tools, Integrations
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.05 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Lookup API - Detect Technologies, Bulk, MCP

**Which CMS, shop platform, analytics, CDN and email provider does a company use?** BuiltWith and Wappalyzer subscriptions cost hundreds of dollars a month, and sales and lead-gen teams need the tech stack of hundreds of prospect companies at once. This actor gives you the same kind of answer per domain for **$2 per 1,000 websites**, with no subscription and no API key.

Give it a list of domains. It makes one polite request to each homepage, matches 3,500+ open technology fingerprints, reads the MX and SPF records to find the email provider, and returns one clean row per domain. Works from the Apify API, from the console, and from **AI agents via MCP**.

### What you get per domain

- **Technologies** with version, confidence, categories and website (CMS, e-commerce, analytics, tag managers, CDN, hosting, web servers, JavaScript frameworks, payment processors, marketing and support tools, security).
- Shortcuts for filters and CRM columns: `cms`, `ecommerce`, `analytics`, `emailProvider`, `cdn`, `technologyNames`, `categories`.
- Company info: page title, meta description, generator and the **company's own social profile links** (LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub).
- **Only-new mode:** alerts when a domain adds or drops a technology (a prospect moves from WooCommerce to Shopify, a competitor installs a new analytics tool).

### Quick start (API)

```bash
curl -X POST "https://api.apify.com/v2/acts/martin10291~website-tech-stack-detector/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["shopify.com", "stripe.com", "hubspot.com"], "includeDns": true}'
```

### Use from AI agents (MCP)

Add this actor to the Apify MCP server at `https://mcp.apify.com` and your agent can call it as a tool:

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

Example prompt: "What CMS and email provider do these 20 companies use?" The agent passes the domains and reads back `cms`, `ecommerce` and `emailProvider` from the rows.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `domains` | array | 5 example sites | Domains or URLs to analyse. The homepage is fetched once per site. |
| `includeDns` | boolean | true | Read MX and TXT records to detect the email provider and SPF senders. |
| `includeCompanyInfo` | boolean | true | Add title, description, generator and social profile links. |
| `minConfidence` | integer | 50 | Only report technologies with at least this confidence (0-100). |
| `maxConcurrency` | integer | 10 | Websites fetched in parallel (1-30). |
| `monitorId` | string | empty | Set it to switch on only-new mode (see below). |

### Output example

```json
{
  "domain": "wordpress.org",
  "finalUrl": "https://wordpress.org/",
  "statusCode": 200,
  "technologies": [
    {"name": "WordPress", "version": null, "confidence": 100, "categories": ["CMS", "Blogs"], "website": "https://wordpress.org"},
    {"name": "Nginx", "version": null, "confidence": 100, "categories": ["Web servers", "Reverse proxies"], "website": "http://nginx.org/en"}
  ],
  "categories": ["Blogs", "CMS", "Reverse proxies", "Web servers"],
  "technologyNames": ["WordPress", "Nginx", "PHP", "MySQL"],
  "cms": "WordPress",
  "ecommerce": null,
  "analytics": [],
  "emailProvider": null,
  "cdn": null,
  "title": "WordPress.org",
  "description": "...",
  "socialLinks": {"twitter": "https://twitter.com/WordPress"},
  "added": null,
  "removed": null,
  "error": null,
  "checkedAt": "2026-11-05T10:00:00Z"
}
```

Rows for sites that cannot be reached are still returned, with `error` set and empty technology lists. A `RUN_SUMMARY` record in the key-value store holds `domainsIn`, `itemsOut`, `failed`, `lastError`, `durationSec` and `fingerprintsVersion`.

### Only-new mode: technology change alerts

Set `monitorId` (for example `"prospects-q4"`). The actor remembers the technology set of every domain under that ID. On the next run it outputs **only domains whose stack changed**, with `added[]` and `removed[]`. The first run with a new ID reports every domain as new (`added` = everything). Schedule the actor daily or weekly and connect a webhook or an Apify integration to get alerts. Use one ID per watch list.

### FAQ

**How accurate is it?** Detection matches the open Wappalyzer fingerprints against the response headers, cookies, HTML, script URLs, meta tags and DNS records. Fingerprints that need a running browser (JavaScript globals, rendered DOM) are not used, so some client-side-only tools will be missed. Use `confidence` to judge weak signals.

**Why is a site missing technologies or showing HTTP 403?** Some sites block automated requests. Their headers and DNS are still analysed, but page-content signals are missing. The actor sends one honest request with its own User-Agent and does not use proxies or evasion.

**Does it crawl the whole site?** No, it analyses the homepage only (or the exact URL you pass).

**Is it legal?** It reads public homepage content and public DNS records of companies. No personal data is collected; social links are the company's own profiles.

**How fast is it?** About 30 domains in 40 seconds at the default concurrency of 10.

**How is it priced?** Pay per result, $2 per 1,000 rows.

### Notice

Fingerprints: Wappalyzer (MIT) v6.10.54, 3,588 technologies. The license text is in `src/data/LICENSE`. Not affiliated with Wappalyzer or BuiltWith.

### Use it as an integration (chain it after another actor)

This actor can run automatically after any actor whose results contain a website or domain.

1. Open the source actor in Apify Console and go to the **Integrations** tab.
2. Click **Add integration** -> **Actor**, pick this actor and set **Dataset** to `{{resource.defaultDatasetId}}` (leave `domains` empty). Set your other options and save.
3. When the source run finishes, this actor reads its dataset and processes every row. The field is detected automatically; set **Dataset field** if your column has another name.

You can also start it by hand with `datasetId` (pick a dataset or paste its ID) to process an existing dataset.

Good sources to chain from:

- compass/crawler-google-places (Google Maps Scraper)
- martin10291/local-business-leads-osm
- martin10291/shopify-app-store-scraper (developerWebsite)
- martin10291/apple-podcasts-scraper (website)
- any lead list with a website/domain column

# Actor input Schema

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

Company websites to analyse, for example shopify.com, stripe.com or https://www.hubspot.com. Bare domains and full URLs are accepted; the homepage is fetched once per site. If left empty, five example sites are used.

## `includeDns` (type: `boolean`):

Also read the domain's MX and TXT records (via DNS) to detect the email provider (Google Workspace, Microsoft 365, ...) and SPF senders.

## `includeCompanyInfo` (type: `boolean`):

Add the page title, meta description, the company's own social profile links found on the homepage (LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub) and the generator meta tag.

## `minConfidence` (type: `integer`):

Only report technologies detected with at least this confidence (0-100). 50 hides weak single-signal guesses; use 100 for the strictest list.

## `maxConcurrency` (type: `integer`):

How many websites are fetched in parallel. 10 is polite; lower it if sites rate-limit you.

## `monitorId` (type: `string`):

Optional. If set, the actor remembers the technology set of each domain under this ID and outputs only domains whose stack changed since the last run, with added\[] and removed\[]. The first run with a new ID reports every domain as new. Leave empty to get all results every run.

## `datasetId` (type: `string`):

Optional. Pick a dataset (or paste its ID) to read the input from, for example the result of Google Maps Scraper. When this actor runs as an integration, the dataset of the triggering run is used automatically (payload.resource.defaultDatasetId), so you can leave this empty. If set, the values found in the dataset replace the main input list.

## `datasetField` (type: `string`):

Optional. The item field that holds the value, for example website, url or videoUrl (dot paths like author.url work). Empty = detected automatically.

## `datasetMaxItems` (type: `integer`):

Upper limit of unique values taken from the dataset, to keep the cost under control.

## Actor input object example

```json
{
  "domains": [
    "shopify.com",
    "stripe.com",
    "wordpress.org",
    "hubspot.com",
    "notion.so"
  ],
  "includeDns": true,
  "includeCompanyInfo": true,
  "minConfidence": 50,
  "maxConcurrency": 10,
  "datasetMaxItems": 1000
}
```

# Actor output Schema

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

One row per website: detected technologies with versions, categories, CMS, e-commerce platform, analytics, email provider, CDN, page title and social links.

# 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",
        "stripe.com",
        "wordpress.org",
        "hubspot.com",
        "notion.so"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("martin10291/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 = { "domains": [
        "shopify.com",
        "stripe.com",
        "wordpress.org",
        "hubspot.com",
        "notion.so",
    ] }

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

```

## MCP server setup

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