# Tech Stack Detector: Wappalyzer & BuiltWith Alt, CSV or Sheet (`nerolabs/tech-stack-detector`) Actor

Wappalyzer and BuiltWith alternative: detects Shopify, WordPress, CMS, analytics, marketing, payments and email hosting behind every website in an Apify dataset, CSV, Excel or Google Sheet. 7,600+ technologies. Inputs: datasetId or fileUrl, websiteField. Charged per site. Agent-ready: x402, MCP.

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

## Pricing

from $3.50 / 1,000 websites

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?

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 (Dataset, CSV or Sheet)

You have a list of companies. Which of them run Shopify? Who is on HubSpot, who is on WordPress, and whose email lives on Google Workspace?

Point this Actor at the list you already have, an Apify dataset, a CSV, an Excel file or a Google Sheet, and it reads each company's homepage and DNS records and hands back **every original column untouched** with the technology columns added alongside: the e-commerce platform, the CMS, analytics, marketing automation, payment providers, hosting, email hosting and more, out of **7,600+ technologies**. No copying a domain column out, no matching results back to company names by hand.

### What it returns

For every row you get:

- **`technologyNames`**: the products the site runs, as one readable line, for example `Shopify, Microsoft 365, Apple Pay, PayPal, Cloudflare, Google Tag Manager`
- **Ready-made columns you can filter a spreadsheet on**: `ecommercePlatform`, `cms`, `analytics`, `marketingAutomation`, `crm`, `paymentProcessors`, `hosting`
- **Email and DNS columns**: `emailHosting` (Google Workspace, Microsoft 365, Zoho and others, proven by the domain's MX record), `emailSending` (SendGrid, Mailgun, Amazon SES and others, from its SPF record), `dnsProvider`, and `dnsVerifiedServices`
- **`technologies`**: the full list, each with its categories, version where the site reveals one, a confidence score, and the evidence it was found from
- **`pageTitle`** and **`pageSizeKb`**: what page was actually analysed, so you can see at a glance that it was the real homepage and not an error page
- **`techStatus`**: `ok`, `no_technologies_found`, `blocked`, `unreachable`, `not_html`, `disallowed_by_robots` or `invalid_url`

Web standards such as HSTS, HTTP/3 and Open Graph are kept in the full list but left out of `technologyNames` and the ready-made columns, because they tell a sales team nothing.

### Find every Shopify store in your list

Put `Shopify` in **Technologies to look for** and set **Rows to keep** to *Only rows matching the filter*. You get back only the matching companies, with all their original columns. It works for anything in the fingerprint set: `WooCommerce`, `HubSpot`, `Klaviyo`, `Salesforce`, `Google Workspace`, `Microsoft 365`, `Webflow`. You can also filter by whole categories such as `Ecommerce` or `Marketing automation`, and ask for sites that use any or all of what you listed. Misspell a name and the run tells you what you probably meant.

### What makes it different from other tech stack detectors

- **It takes your whole list.** None of the ten most-used tech stack detectors on the Store accepts a dataset, file or sheet: each takes a typed-in list of URLs and hands back a bare list you then have to re-join to your own data. This one reads a dataset, a file or a Google Sheet directly and keeps every column you had.
- **It reads DNS as well as the page.** That is how it can tell you a company's email runs on Microsoft 365 while its shop runs on Shopify, and which services the company has verified its domain with.
- **It says how it knows.** Every technology carries its evidence (a script source, a cookie, a meta tag, a DNS record) and whether it was seen on the website itself or only in DNS.
- **It catches tools installed by a loader snippet.** Tag managers, email-capture tools and chat widgets are usually injected by a small inline script. The script address inside that snippet is picked up and counted, at a reduced confidence of 75 so you can tell it apart.
- **You never pay for a site it could not read.** Blocked, unreachable, non-HTML and robots-disallowed sites are free.

### Example

A real row from the default example input:

```json
{
  "company": "Allbirds",
  "website": "allbirds.com",
  "techStatus": "ok",
  "pageTitle": "Allbirds: Comfortable, Sustainable Shoes & Apparel",
  "technologyNames": "Shopify, Microsoft 365, Apple Pay, PayPal, Cloudflare, Google Tag Manager, Swiper, Attentive",
  "ecommercePlatform": "Shopify",
  "paymentProcessors": "Apple Pay, PayPal",
  "emailHosting": "Microsoft 365",
  "dnsVerifiedServices": "Cursor, DocuSign, Miro",
  "technologies": [
    { "name": "Shopify", "categories": ["Ecommerce"], "confidence": 100, "detectedOn": "website", "evidence": ["cookie", "meta", "script-loader", "script-src"] },
    { "name": "Microsoft 365", "categories": ["Webmail", "Email"], "confidence": 100, "detectedOn": "dns", "evidence": ["dns-mx", "dns-txt"] },
    { "name": "Attentive", "categories": ["Personalisation"], "confidence": 75, "detectedOn": "website", "evidence": ["script-loader"] }
  ],
  "fetchedVia": "direct",
  "pageRenderedWithJavascript": false
}
```

The run summary also lists the **most common technologies across your whole list**, which doubles as a quick market map: how many of your prospects are on Shopify versus WooCommerce, or on HubSpot versus Klaviyo.

### How each site is checked

1. **robots.txt** is read first and honoured by default: a site that disallows its homepage for automated agents is skipped and not charged.
2. **The homepage** is fetched once, exactly as the server sends it, and matched against the fingerprint set: script sources, inline scripts, HTML, meta tags, cookies, response headers, page structure and the URL itself.
3. **DNS records** (MX, TXT, NS, SOA, and the web host's CNAME) are looked up, which adds email hosting, email sending, DNS provider and verified services at no extra charge. You can switch this off.

The page's JavaScript is **not** run, which is what keeps this fast and cheap, and every row says so in `pageRenderedWithJavascript: false`. The trade-off is honest and worth knowing: a tool that only appears after scripts run can be missed, so a missing technology is not proof that a site does not use it.

**Blocked sites.** Some large brands refuse automated requests. Those rows come back as `blocked` and cost nothing. If your list is heavy on big brands you can switch on *Retry blocked sites through a residential proxy*: a blocked site is retried once through Apify's residential proxy, and only a retry that succeeds is charged. This helps against sites that block cloud-server addresses; sites with advanced bot protection, which is most of the biggest brands, will usually still refuse. Most small and mid-sized business websites are read directly with no trouble.

### Pricing

- **$0.005** per website analysed
- **Nothing** for a site that could not be reached, blocked the request, was not an HTML page, or was disallowed by its own robots.txt
- **$0.01** per CSV or Excel export, **$0.02** per webhook delivery
- **$0.005** extra per blocked site successfully read through the residential proxy, only if you switch that option on

So **1,000 websites cost about $5**, and 100 cost about 50 cents. DNS lookups, the filter, the summary and the market map are included.

Store discount tiers are on from day one, so Bronze, Silver and Gold plan customers pay 10, 20 and 30 percent less.

### FAQ

**Is this like Wappalyzer or BuiltWith?**
It does the same kind of job, and it is not affiliated with either. The difference is the input and the output: you give it your list as it is, a dataset, file or sheet, and you get that same list back with the technology columns added, plus email hosting from DNS.

**It missed a tool I know they use. Why?**
Most likely that tool is only loaded after the page's JavaScript runs, for example a chat widget or an A/B testing script pulled in by a tag manager. This Actor reads the page as the server sends it, which is fast and cheap but cannot see those. Tools installed by an inline loader snippet are still caught.

**Why does it say Microsoft 365 when their website runs on Shopify?**
Those are two different things. `ecommercePlatform` is what the website runs on; `emailHosting` is where the company's email lives, read from the domain's MX record. Both are useful and they are reported separately.

**What are "DNS-verified services"?**
Companies add a DNS record to prove they own their domain to services such as Zoom, DocuSign, Atlassian or Salesforce. Seeing one means the company has an account there. It is kept in its own column because it is about the company, not about what the website runs on.

**Why is a technology's confidence 75?**
It was found only as a script address inside an inline loader snippet, which is strong evidence but not proof the tool actually runs. Set a minimum confidence if you only want certain detections.

**Where does the fingerprint data come from?**
From the open-source webappanalyzer fingerprint set, pinned to a known version so every run is reproducible; the exact source version is recorded in each run's summary. One over-broad upstream rule is corrected: an Apple domain-verification record is not treated as proof of iCloud Mail.

**Can an AI agent run it?**
Yes. It uses pay-per-event pricing with limited permissions, so it can be called through Apify's MCP server and paid for with x402.

### The rest of the toolkit

The natural pair is **[Website Contact Finder](https://apify.com/nerolabs/website-contact-finder)**: run both on the same list, or chain them with the Pipeline Runner, to get every Shopify store in your list together with its business inbox. Then **[Email List Cleaner & Validator](https://apify.com/nerolabs/email-list-cleaner)** checks the addresses and **[Phone Number Validator & Cleaner](https://apify.com/nerolabs/phone-number-validator)** checks the numbers.

For the data itself: [Dataset Cleaner & Exporter](https://apify.com/nerolabs/dataset-cleaner-exporter), [Filter & Transform](https://apify.com/nerolabs/dataset-filter-transform), [Join & Merge](https://apify.com/nerolabs/dataset-join-merge), [Aggregate, Group By & Pivot](https://apify.com/nerolabs/dataset-aggregate-pivot), [Diff & Change Detector](https://apify.com/nerolabs/dataset-diff-detector), [AI Enrich](https://apify.com/nerolabs/dataset-ai-enrich), [Charts & Report](https://apify.com/nerolabs/dataset-charts-report), [to Postgres, Supabase & MySQL](https://apify.com/nerolabs/dataset-to-database), [to REST API](https://apify.com/nerolabs/dataset-to-rest-api), and [Actor Pipeline Runner](https://apify.com/nerolabs/actor-pipeline-runner) to chain them in one call.

***

If this saved you opening a few hundred websites to check what they run on, a review on the Store page helps a lot. If something looks wrong, open an issue on the Issues tab and I will answer personally.

# Actor input Schema

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

An Apify dataset whose rows each hold a website or domain. Use the picker so the run is allowed to read it. Leave empty to use a file URL or inline data instead.

## `fileUrl` (type: `string`):

A public link to a CSV, TSV, Excel (.xlsx), JSON or JSON Lines file, or a Google Sheet shared as 'anyone with the link'. Used when no dataset is set.

## `data` (type: `array`):

Rows as a JSON array, each with a website or domain field. Used when neither a dataset nor a file URL is set.

## `fileFormat` (type: `string`):

How to read the file URL. 'Detect automatically' works from the extension, the content type and the first bytes.

## `websiteField` (type: `string`):

The column holding each company's website or domain, for example 'website' or 'domain'. Leave empty to detect it automatically. Full URLs, bare domains and even an email address all work.

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

Exact technology names, for example 'Shopify', 'WordPress', 'WooCommerce', 'HubSpot', 'Klaviyo', 'Google Workspace' or 'Microsoft 365'. Matching ignores case. A name that is not in the fingerprint set is reported with suggestions and ignored.

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

Match any technology in these categories, for example 'Ecommerce' or 'Marketing automation'.

## `matchMode` (type: `string`):

Whether a site must use any one of the listed technologies and categories, or all of them.

## `keep` (type: `string`):

Which rows to write out. Every row is still checked and every analysed site is still charged; this only controls what is returned.

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

Drop detections below this confidence. Each technology comes with a confidence score; one seen only inside an inline loader script scores 75, and implied technologies inherit a lower score.

## `checkDns` (type: `boolean`):

ON by default. Reads the domain's MX, TXT, NS and SOA records, which is how email hosting (Google Workspace, Microsoft 365), email sending services and DNS providers are detected, and which services the company has verified its domain with. No extra charge.

## `respectRobotsTxt` (type: `boolean`):

ON by default. A site whose robots.txt disallows its homepage for automated agents is reported as 'disallowed\_by\_robots' and not charged.

## `retryBlockedWithProxy` (type: `boolean`):

OFF by default. Large brands often refuse ordinary requests. When on, a blocked site is retried once through Apify's residential proxy; each site that then succeeds costs one extra 'residential retry' event on top of the normal site charge. A retry that still fails costs nothing.

## `concurrency` (type: `integer`):

How many websites are checked in parallel.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for a homepage before reporting the site as unreachable.

## `exportFormats` (type: `array`):

Also write the results as a downloadable CSV and/or Excel file, with a public link in the run summary.

## `outputDatasetName` (type: `string`):

Optional. The results are also appended to this named dataset, so repeated runs build up one list. Not charged again.

## `webhookUrl` (type: `string`):

Optional. When the run finishes, a JSON summary (counts, the most common technologies, download links) is POSTed here, for Zapier, Make, n8n, Slack or your own API.

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

Check at most this many rows. Leave empty to check them all (up to 50,000 per run).

## Actor input object example

```json
{
  "data": [
    {
      "company": "Allbirds",
      "website": "allbirds.com"
    },
    {
      "company": "WordPress.org",
      "website": "wordpress.org"
    },
    {
      "company": "Webflow",
      "website": "webflow.com"
    },
    {
      "company": "Electronic Frontier Foundation",
      "website": "https://www.eff.org"
    }
  ],
  "fileFormat": "auto",
  "matchMode": "any",
  "keep": "all",
  "minConfidence": 0,
  "checkDns": true,
  "respectRobotsTxt": true,
  "retryBlockedWithProxy": false,
  "concurrency": 5,
  "requestTimeoutSecs": 20,
  "exportFormats": []
}
```

# Actor output Schema

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

Every original row with the technologies detected on its website added.

## `techSummary` (type: `string`):

Statuses, the most common technologies across the list, the filter result and the detection note.

# 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 = {
    "data": [
        {
            "company": "Allbirds",
            "website": "allbirds.com"
        },
        {
            "company": "WordPress.org",
            "website": "wordpress.org"
        },
        {
            "company": "Webflow",
            "website": "webflow.com"
        },
        {
            "company": "Electronic Frontier Foundation",
            "website": "https://www.eff.org"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerolabs/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 = { "data": [
        {
            "company": "Allbirds",
            "website": "allbirds.com",
        },
        {
            "company": "WordPress.org",
            "website": "wordpress.org",
        },
        {
            "company": "Webflow",
            "website": "webflow.com",
        },
        {
            "company": "Electronic Frontier Foundation",
            "website": "https://www.eff.org",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("nerolabs/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 '{
  "data": [
    {
      "company": "Allbirds",
      "website": "allbirds.com"
    },
    {
      "company": "WordPress.org",
      "website": "wordpress.org"
    },
    {
      "company": "Webflow",
      "website": "webflow.com"
    },
    {
      "company": "Electronic Frontier Foundation",
      "website": "https://www.eff.org"
    }
  ]
}' |
apify call nerolabs/tech-stack-detector --silent --output-dataset

```

## MCP server setup

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