# Tech Stack Detector — BuiltWith & Wappalyzer Alternative (`diopside/tech-stack-detector`) Actor

Drop-in for nexgendata/wappalyzer-replacement at 1/50th the price. 275+ technologies: CMS, ecommerce, JS frameworks, analytics, ad pixels, payments, CDN, hosting, chat, reviews, consent. Every detection names the header, cookie or script it matched. One row per domain; no data, no charge.

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

## Pricing

from $2.00 / 1,000 domain 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 — BuiltWith & Wappalyzer Alternative

**Drop-in compatible with [nexgendata/wappalyzer-replacement](https://apify.com/nexgendata/wappalyzer-replacement) — 50× cheaper, and it actually finds the stack.** Same input field names, same output keys, same shapes. Change the actor id and your code keeps working.

Point it at a list of domains and get back the technologies behind each one: CMS, ecommerce platform, JavaScript frameworks, analytics, ad pixels, payment processors, CDN, hosting, live chat, review widgets and consent tools. No API key, no login, no proxy required.

### Why this one

We ran the leading incumbent against `allbirds.com` on 2026-09-21, paid its **$0.10**, and got four technologies back:

> Cloudflare, Content Security Policy, HSTS, HTTP/3

Every one of those is a response header. It did not notice that allbirds.com **is a Shopify store** — the single most valuable fact about that domain for anyone doing lead qualification, and one that is sitting in plain sight in a `_shopify_y` cookie and a `cdn.shopify.com` script tag.

Same domain, same moment, this actor at **$0.002**:

> Shopify, Shop Pay, Cloudflare, Google Tag Manager, Swiper, Tailwind CSS, Subresource Integrity, Content Security Policy, HSTS, HTTP/3, X-Frame-Options

Three things follow from that, and they are the whole pitch:

1. **It reads the page, not just the headers.** 275+ fingerprints across 33 categories, matched against response headers, cookies, `meta` tags, every script/stylesheet/iframe the page loads, the raw HTML, and `robots.txt`.
2. **Every detection shows its work.** Each technology carries the exact signal that produced it — `cookie: _shopify_y`, `header: x-powered-by: Next.js`, `meta generator: WordPress 6.5`, `script: https://cdn.shopify.com/...`. You can audit a row instead of trusting it. No other actor in this cluster does this.
3. **It costs $0.002 per domain instead of $0.10.** A 5,000-domain enrichment run is $10 here and $500 there.

### Reliability

- **Every input gets exactly one output row, in your order** — including the domains that turn out to be dead. An actor that silently drops unreachable domains turns your join back onto your own list into a guessing game. Failures come back with `error` filled in and `status_code: null`.
- **You are not charged for a row that carries no data.** DNS failures, timeouts and empty responses ship free. You pay only for domains that answered *and* produced at least one detection.
- **A real Chrome TLS fingerprint.** Measured on a 15-site sample during the build: plain `httpx` got 14/15, `curl_cffi` with Chrome impersonation got 15/15. Sites that fingerprint TLS (nytimes.com, for one) answer this actor and refuse a naive HTTP client.
- **Byte-capped reads.** Homepages are getting absurd — cloudflare.com is 1.3 MB. Fingerprints live in the head and the first screens of markup, so each page stops downloading at `maxBytesPerPage` (400 KB by default). That is what keeps the price at $0.002.
- **A bad `robots.txt` never costs you the page.** The robots read is a bonus signal; if it fails, the row still ships.
- **No proxy needed.** Verified on the Apify platform with the proxy switched off. Turn `proxyConfiguration` on only if you are scanning at a scale where a target rate-limits the platform's IP range.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `urls` | array / string | `["stripe.com", "allbirds.com", "wordpress.org"]` | Bare hosts or full URLs. Also accepted as `startUrls`, `domains`, `websites`, `targetUrls`, `url`, `domain`. Comma- or newline-separated strings are split for you. |
| `categories_filter` | array | `[]` | Only return these categories. Case-, space- and dash-insensitive, so `E-Commerce` and `ecommerce` both work. |
| `include_confidence` | boolean | `true` | Add a 0–100 score to each detection. |
| `include_versions` | boolean | `true` | Parse version numbers where a reliable signal exists. Never guessed. |
| `timeout_seconds` | integer | `15` | Per-request timeout, 3–60. |
| `checkRobots` | boolean | `true` | *Addition.* Also read `/robots.txt`: confirms themed-over CMS platforms and fills `sitemap_urls`. |
| `maxConcurrency` | integer | `8` | *Addition.* Domains analysed in parallel, 1–25. |
| `maxBytesPerPage` | integer | `400000` | *Addition.* Stop reading a page after this many bytes. |
| `maxItems` | integer | — | *Addition.* Hard cap on how many websites to analyse. |
| `proxyConfiguration` | object | `{"useApifyProxy": false}` | *Addition.* Off by default. |

The first five rows are the incumbent's fields, with the incumbent's names and defaults.

### Output

```json
{
  "url": "https://allbirds.com",
  "status_code": 200,
  "tech_count": 11,
  "categories": {
    "CDN": ["Cloudflare"],
    "Ecommerce": ["Shopify"],
    "Payment Processors": ["Shop Pay"],
    "Security": ["Content Security Policy", "HSTS", "Subresource Integrity", "X-Frame-Options"]
  },
  "technologies": [
    { "name": "Cloudflare", "category": "CDN", "evidence": "header: cf-ray", "version": null, "confidence": 100, "website": "https://cloudflare.com" },
    { "name": "Shopify", "category": "Ecommerce", "evidence": "cookie: _shopify_y", "version": null, "confidence": 95, "website": "https://shopify.com" },
    { "name": "Shop Pay", "category": "Payment Processors", "evidence": "script: https://shop.app/checkouts/internal/preloads.js", "version": null, "confidence": 90, "website": null }
  ],
  "scan_time_ms": 1212,
  "final_url": "https://www.allbirds.com/",
  "tech_names": "Cloudflare, Content Security Policy, HSTS, HTTP/3, Shopify, ...",
  "category_list": "CDN, Ecommerce, Miscellaneous, Payment Processors, Security",

  "tech_names_list": ["Cloudflare", "Content Security Policy", "..."],
  "category_names": ["CDN", "Ecommerce", "Miscellaneous", "Payment Processors", "Security"],
  "top_technologies": ["Cloudflare", "Shopify", "Shop Pay"],
  "redirected": true,
  "page_title": "Allbirds: Comfortable, Sustainable Shoes & Apparel",
  "page_description": "Shop the world's most comfortable shoes...",
  "server": "cloudflare",
  "sitemap_urls": ["https://www.allbirds.com/sitemap.xml"],
  "html_bytes": 409426,
  "error": null,
  "scanned_at": "2026-09-21T11:42:07+00:00"
}
```

Everything above the blank line is the incumbent's key set, byte for byte, including `tech_names` as a comma-joined string. Everything below it is an addition — `tech_names_list` is the same data as an array if you would rather not split a string.

#### Confidence

| Score | Source |
|---|---|
| 100 | A response header. The server told us. |
| 95 | A cookie name, or a `meta generator` tag. |
| 90 | A script, stylesheet or iframe the page loads. |
| 85 | A pattern in the raw HTML. |
| 80 | A line in `robots.txt`. |
| 60 | Implied by another technology (WooCommerce ⇒ WordPress ⇒ PHP). |

### Switching from nexgendata/wappalyzer-replacement

Change the actor id. That is the whole migration:

```diff
- const run = await client.actor('nexgendata/wappalyzer-replacement').call({
+ const run = await client.actor('diopside/tech-stack-detector').call({
    urls: ['https://allbirds.com', 'https://gymshark.com'],
    include_confidence: true,
    include_versions: true,
  });
```

`urls`, `categories_filter`, `include_confidence`, `include_versions` and `timeout_seconds` mean the same things. `url`, `status_code`, `tech_count`, `categories`, `technologies`, `scan_time_ms`, `final_url`, `tech_names` and `category_list` come back with the same names and the same types. Rows you used to lose for unreachable domains now arrive with `error` set, so expect one row per input rather than one row per *reachable* input.

### What it detects

33 categories, 275+ technologies:

CMS (WordPress, Drupal, Joomla, Ghost, Craft, Sitecore, AEM, Contentful, Sanity, Storyblok, Wix, Squarespace, Webflow, Duda, Framer, HubSpot CMS) · Ecommerce (Shopify, WooCommerce, Magento, BigCommerce, PrestaShop, Shopware, Salesforce Commerce Cloud, SAP Commerce Cloud, VTEX, Ecwid, Snipcart, Recharge) · JavaScript frameworks (React, Vue, Angular, Svelte, Preact, Alpine, htmx, Stimulus, Turbo, Ember, Lit) · Web frameworks (Next.js, Nuxt, Remix, SvelteKit, Astro, Gatsby, Rails, Django, Laravel, Symfony, Express, Flask, ASP.NET, Spring, Phoenix) · Static site generators (Hugo, Jekyll, Eleventy, Docusaurus, Hexo) · Analytics (GA4, Adobe, Matomo, Plausible, Fathom, Umami, PostHog, Mixpanel, Amplitude, Heap, Hotjar, Clarity, Segment, FullStory, LogRocket, Contentsquare, New Relic, Datadog, Sentry, Pendo) · Tag managers · Advertising pixels (Meta, TikTok, LinkedIn, Pinterest, Reddit, Snap, X, Bing, Criteo, Taboola, Outbrain, AdRoll, Amazon) · Payment processors (Stripe, PayPal, Braintree, Adyen, Klarna, Afterpay, Affirm, Square, Checkout.com, Mollie, Razorpay, Shop Pay, Apple Pay, Google Pay, Amazon Pay) · Marketing automation & CRM (HubSpot, Marketo, Pardot, Salesforce, Klaviyo, Mailchimp, ActiveCampaign, Braze, Customer.io, Omnisend, Attentive, Iterable) · Live chat (Intercom, Drift, Zendesk, Crisp, Tawk.to, LiveChat, Freshchat, Tidio, Gorgias, Olark) · Reviews (Yotpo, Trustpilot, Bazaarvoice, Judge.me, Okendo, Loox, Stamped) · Search (Algolia, Searchspring, Klevu, Coveo) · A/B testing (Optimizely, VWO, AB Tasty, Dynamic Yield, Kameleoon) · Cookie consent (OneTrust, Cookiebot, CookieYes, Usercentrics, Didomi, Osano, TrustArc, Iubenda) · CDN & hosting (Cloudflare, Akamai, Fastly, CloudFront, Vercel, Netlify, Heroku, GitHub Pages, Firebase, Render, Fly.io, WP Engine, Kinsta, Pantheon, Bunny, jsDelivr, Cloudinary, imgix) · Web servers (nginx, Apache, IIS, LiteSpeed, OpenResty, Caddy, Envoy, Varnish) · Security & bot protection (CSP, HSTS, X-Frame-Options, SRI, reCAPTCHA, hCaptcha, Turnstile, Cloudflare Bot Management, DataDome, HUMAN, Imperva, Akamai Bot Manager, Sucuri, Wordfence) · Fonts, UI frameworks, video players, maps, JS libraries and build tools.

### Use cases

- **Lead qualification.** Filter a prospect list down to the Shopify stores, or the sites running a competitor's checkout. `top_technologies` is the field to put in a scoring rule.
- **Competitive research.** Watch when a set of sites migrates CMS, adds a consent manager, or swaps analytics vendors. `scanned_at` makes a time series out of repeat runs.
- **Agency prospecting.** Find sites still on an old stack, or sites with no consent tool in a GDPR market.
- **Security posture snapshots.** CSP, HSTS, X-Frame-Options, SRI and bot-protection vendor across a whole portfolio in one run.
- **Enrichment pipelines.** One row per input domain, always, so it drops straight into a join.

### Sizes and cost

| Input | Runtime | Cost |
|---|---|---|
| 5 domains | ~10 s | $0.01 |
| 100 domains | ~40 s | $0.20 |
| 1,000 domains | ~6 min | $2.00 |
| 10,000 domains | ~55 min | $20.00 |

Pay per event: $0.002 per domain analysed, plus a $0.00005 actor start. Rows with an `error` are not charged. The same 1,000 domains cost $100 on the incumbent.

### Notes

- Public site fingerprinting only. This actor reads a homepage and `/robots.txt` — the same two things any browser or search crawler reads. No personal data, no logins, no paywalled content.
- One request per domain (two with `checkRobots`). It is not a crawler; it does not walk your targets' sites.
- `version` is `null` unless a version genuinely appears in a header, script URL or generator tag. It is never inferred.

# Actor input Schema

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

One or more websites. Accepts bare hosts ('stripe.com') or full URLs ('https://stripe.com/pricing') — the scheme is added when it is missing. Also accepted under the aliases `startUrls`, `domains`, `websites` and `targetUrls`, so input built for another tech-stack actor works unchanged.

## `startUrls` (type: `array`):

Alias of `urls`. Accepts plain strings or `{ "url": "..." }` objects, so a Website Content Crawler-style input works here too.

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

Alias of `urls`, for bare domain lists exported from a CRM.

## `categories_filter` (type: `array`):

Only return technologies in these categories. Examples: 'CMS', 'Ecommerce', 'Analytics', 'CDN', 'JavaScript Frameworks', 'Payment Processors', 'Marketing Automation', 'Security'. Matching ignores case, spaces and dashes, so 'E-Commerce' and 'ecommerce' both work. Leave empty for everything.

## `include_confidence` (type: `boolean`):

Each detected technology carries a 0–100 confidence score. A response header scores 100, a cookie or meta generator 95, a script the page loads 90, raw HTML 85, robots.txt 80, and a technology that was only implied by another 60.

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

Parse version numbers out of matched headers, script URLs and meta generator tags — 'nginx/1.24.0', '/jquery-3.7.1.min.js', 'WordPress 6.5'. `version` is null when no reliable signal is present; it is never guessed.

## `timeout_seconds` (type: `integer`):

Per-request timeout. Raise it for slow sites; lower it to cut short the dead domains in a big list.

## `checkRobots` (type: `boolean`):

Fetch /robots.txt as a second, cheap signal. It confirms CMS platforms whose markup has been themed away (Disallow: /wp-admin/, Shopify's /a/downloads/-/) and fills the `sitemap_urls` field. Adds one small request per domain.

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

How many websites to analyze at once. Each one is a different host, so this is parallelism across sites, not pressure on any single site.

## `maxBytesPerPage` (type: `integer`):

Stop downloading a homepage after this many bytes. Fingerprints live in the head and the first screens of markup, so the default reads enough of a 1.3 MB homepage without paying for the rest. Raise it if a site puts its scripts at the very bottom of a huge document.

## `maxItems` (type: `integer`):

Hard cap on how many of the supplied websites to analyze. Leave empty for all of them.

## `proxyConfiguration` (type: `object`):

Optional. This actor reads public homepages with a real Chrome TLS fingerprint and does not need a proxy for ordinary sites — it is measured working with the proxy off. Turn the Apify proxy on if you are scanning thousands of domains, or if a target rate-limits the platform's IP range.

## Actor input object example

```json
{
  "urls": [
    "stripe.com",
    "allbirds.com",
    "wordpress.org"
  ],
  "startUrls": [],
  "domains": [],
  "categories_filter": [],
  "include_confidence": true,
  "include_versions": true,
  "timeout_seconds": 15,
  "checkRobots": true,
  "maxConcurrency": 8,
  "maxBytesPerPage": 400000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `websites` (type: `string`):

All tech-stack records. Append ?format=csv for CSV.

## `datasetUrl` (type: `string`):

The default dataset.

# 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": [
        "stripe.com",
        "allbirds.com",
        "wordpress.org"
    ],
    "startUrls": [],
    "domains": [],
    "categories_filter": [],
    "include_confidence": true,
    "include_versions": true,
    "timeout_seconds": 15,
    "checkRobots": true,
    "maxConcurrency": 8,
    "maxBytesPerPage": 400000,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("diopside/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": [
        "stripe.com",
        "allbirds.com",
        "wordpress.org",
    ],
    "startUrls": [],
    "domains": [],
    "categories_filter": [],
    "include_confidence": True,
    "include_versions": True,
    "timeout_seconds": 15,
    "checkRobots": True,
    "maxConcurrency": 8,
    "maxBytesPerPage": 400000,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("diopside/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": [
    "stripe.com",
    "allbirds.com",
    "wordpress.org"
  ],
  "startUrls": [],
  "domains": [],
  "categories_filter": [],
  "include_confidence": true,
  "include_versions": true,
  "timeout_seconds": 15,
  "checkRobots": true,
  "maxConcurrency": 8,
  "maxBytesPerPage": 400000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call diopside/tech-stack-detector --silent --output-dataset

```

## MCP server setup

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