# Bulk Tech Stack Detector (`eliai/bulk-tech-stack-detector`) Actor

Paste a list of domains and get what each one runs: CMS, framework, hosting, analytics, payments, support widgets. Every detection returns the evidence that proved it plus a confidence level, so a wrong row is auditable. From $0.002 per domain on paid plans; unreachable domains never charged.

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

## Pricing

from $8.00 / 1,000 analysed domains

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

## Bulk Tech Stack Detector

Paste a list of domains, get back what each one runs — CMS, framework, hosting, analytics,
payments, support widgets — with **the evidence that proved each detection** and a confidence
level.

Built for lists, not single lookups. Unreachable domains are reported and **never charged**.

***

### Why the evidence matters

Most detectors hand you `WordPress: true`. When that's wrong on 200 rows of a 10,000-row list, you
have no way to find which 200. Every detection here returns what proved it:

| Technology | Confidence | Evidence |
|---|---|---|
| Shopify | high | `cdn.shopify.com` |
| Vercel | high | `server: Vercel` |
| WordPress | high | `/wp-content/` |
| jQuery | low | `jquery-3.6.0.min.js` |

Now a row is auditable in two seconds instead of being taken on faith.

### Confidence is not decoration

- **high** — the site declares it, or a first-party branded asset path proves it (a `generator`
  meta tag, `cdn.shopify.com`, a `server` header)
- **medium** — a characteristic asset path or header
- **low** — a weaker heuristic, worth reporting but not worth filtering a campaign on

Flattening these into a single boolean is how a bulk list becomes untrustworthy. You can filter on
`confidence` and decide your own bar.

### Three things this deliberately does not do

**No vulnerability claims.** Some detectors report "WordPress 5.2 — 14 known CVEs". A version
string in HTML is routinely stale, cached, or spoofed, and converting it into a security assertion
is unfounded. We report the version we saw and where we saw it. Nothing further.

**No guessing.** A site we can't classify comes back with an empty technology list and
`classified: false`. Silent guessing is exactly what makes bulk output useless at scale — one
plausible-looking wrong row poisons trust in all 10,000.

**No double-counting.** `example.com` and `www.example.com` are one company. We follow redirects to
the final URL and report both it and whether a redirect happened, so your list doesn't
double-count or silently miss.

### What it detects

**CMS & builders** — WordPress, Drupal, Joomla, Ghost, Shopify, Wix, Squarespace, Webflow
**Frameworks** — Next.js, Nuxt, Gatsby, Astro, React, Vue
**Hosting & CDN** — Cloudflare, Vercel, Netlify, Fastly, CloudFront, Nginx, Apache
**Analytics** — GA4, Google Tag Manager, Meta Pixel, Hotjar, Plausible, Segment
**Marketing & support** — HubSpot, Klaviyo, Mailchimp, Intercom, Zendesk, Drift, Crisp
**Payments & security** — Stripe, PayPal, reCAPTCHA, Cloudflare Turnstile
Plus any self-declared `generator` meta tag, reported verbatim.

### Input

```json
{ "domainsText": "shopify.com\nvercel.com\nexample.com" }
```

Separators can be new lines, commas or spaces; `https://` is optional and duplicates are removed.

| Field | Type | Default | Notes |
|---|---|---|---|
| `domainsText` | string | — | Pasted list |
| `domains` | array | — | For API use |
| `domain` | string | — | A single domain |
| `maxDomains` | integer | 100 | Cap on domains, and therefore on spend |

### Output

```json
{
  "domain": "shopify.com",
  "ok": true,
  "finalUrl": "https://www.shopify.com/",
  "redirected": true,
  "technologyCount": 2,
  "technologies": [
    { "technology": "Shopify", "category": "Ecommerce", "confidence": "high", "evidence": "cdn.shopify.com" },
    { "technology": "Cloudflare", "category": "CDN", "confidence": "high", "evidence": "server: cloudflare" }
  ],
  "byCategory": { "Ecommerce": ["Shopify"], "CDN": ["Cloudflare"] },
  "classified": true
}
```

### For agents and automation

- **Capability:** bulk detection of web technologies across a domain list
- **Required input:** one of `domainsText`, `domains`, `domain`
- **Returns:** one record per domain plus a `SUMMARY`; `byCategory` is pre-grouped for filtering
- **Bounded:** `maxDomains` caps the run and the spend
- **Side effects:** none — fetches each homepage once, stores nothing externally
- **Honest unknowns:** `classified: false` with an empty list rather than a guess

### Pricing

Pay per domain successfully analysed. Unreachable domains are free.

### FAQ

**How do I find out what technology a list of websites uses?** Paste the domains into `domainsText` (newlines, commas, or spaces — duplicates removed) and each record returns the detected CMS, framework, hosting, analytics, payments, and support tools, grouped in `byCategory` for filtering.

**Why does every detection include evidence?** So a wrong row is auditable in seconds. `WordPress: true` on its own is faith; `WordPress — /wp-content/` is checkable. On a 10,000-row list, that difference is whether you can trust the output at all.

**What does the confidence level mean?** `high` = the site declares it or a first-party branded asset proves it; `medium` = a characteristic path or header; `low` = a weak heuristic worth seeing but not worth filtering a campaign on. Filter on it and set your own bar.

**Can it tell me if a site's stack is vulnerable?** Deliberately not — version strings in HTML are routinely stale or spoofed, and turning one into a CVE claim would be unfounded. You get the version seen and where it was seen, nothing invented.

**How does it handle sites it can't classify?** `classified: false` with an empty technology list — an honest unknown instead of a plausible guess, because one wrong-but-plausible row poisons trust in the whole list.

### For AI agents

This Actor is built to be called by software, not just by people.

- **Mount it directly as an MCP tool** — no Store search, no ranking, just this one tool:
  `https://mcp.apify.com/?actors=eliai/bulk-tech-stack-detector`
- **Or call it over HTTP** and get the results in the same request:
  `POST https://api.apify.com/v2/acts/eliai~bulk-tech-stack-detector/run-sync-get-dataset-items`
- **Pay with x402, without an Apify account.** This Actor is whitelisted for agentic payments, so an agent holding USDC on Base can buy a prepaid token and spend it here. The minimum purchase is $1, the token balance is an absolute spending cap, and it expires 14 days after purchase.
- **Costs are predictable before you call.** Pricing is pay-per-event (see Pricing above), so an agent can budget a run in advance instead of discovering the bill afterwards.
- **Send only the field you mean.** If you pass the bulk field, it is used on its own; the single-value field is a fallback, never merged into your request. You are charged for the items you sent and nothing else.

# Actor input Schema

## `domainsText` (type: `string`):

One domain per line, or separated by commas or spaces. Duplicates removed automatically. https:// optional. When you set this, it is used on its own and the single-value field above is ignored, so you are only charged for the items you sent.

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

For API and integration use.

## `domain` (type: `string`):

Analyse a single domain.

## `maxDomains` (type: `integer`):

Safety cap. You are charged per domain successfully analysed, so this is also your budget cap.

## Actor input object example

```json
{
  "domainsText": "shopify.com\nvercel.com\nwordpress.org",
  "domains": [],
  "domain": "shopify.com",
  "maxDomains": 100
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "domainsText": `shopify.com
vercel.com
wordpress.org`,
    "domains": [],
    "domain": "shopify.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/bulk-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 = {
    "domainsText": """shopify.com
vercel.com
wordpress.org""",
    "domains": [],
    "domain": "shopify.com",
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/bulk-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 '{
  "domainsText": "shopify.com\\nvercel.com\\nwordpress.org",
  "domains": [],
  "domain": "shopify.com"
}' |
apify call eliai/bulk-tech-stack-detector --silent --output-dataset

```

## MCP server setup

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