# Tech Stack Detector for Any List of Websites (`usta/tech-stack-detector`) Actor

Paste a list of website addresses and get one row per site naming the software it runs on: store platform, CMS, analytics, email and chat tools, payments, CDN and more. Public home page only, no login. Pay per site checked.

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

## Pricing

$4.00 / 1,000 result rows

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 for Any List of Websites

**A tech stack detector, website technology lookup, and Wappalyzer / BuiltWith alternative you can run in bulk:** paste a list of company websites and get back, for each one, the shop platform, CMS, analytics, ads pixels, and other technology this Actor could read from that site's own public home page.

### Introduction

Every site gets one live, signed-out `GET` of its home page — no login, no
browser, no JavaScript execution — and the response is matched against a
rules file this Actor ships (`data/fingerprints.json`, 134 technologies
across 14 categories), written from public knowledge of each product's own
documented script hosts, generator tags, cookie-name conventions, and
response headers. Nothing is stored between runs, robots.txt is honoured
for every site before it is fetched, and a site this Actor could not read
(blocked, timed out, 404, etc.) gets one honest error row instead of a
guess.

### Input

| Field | What it does |
| --- | --- |
| `urls` | The website addresses to check. One per item; a bare domain like `example.com` is treated as `https://example.com`. |
| `maxItems` | Hard ceiling on rows returned (1-2000). Also the spend cap, since every row bills. Default 50. |
| `proxyConfiguration` | When set, every request goes through that proxy. Off by default. |

### Output — one row per site

| Field | What it holds |
| --- | --- |
| `site` | The address you gave, normalised to `https://` if you didn't include a scheme. |
| `final_url` | Where the request actually landed after any redirect. |
| `http_status` | The HTTP status code of the home page fetch, or `null` if the site could not be reached. |
| `technologies` | List of `{name, category, confidence, evidence}`. `confidence` is `high` or `medium`. `evidence` is the actual matched text (header value, cookie name, generator tag, or script/link address) found on that page, up to 120 characters. |
| `categories_found` | The distinct technology categories detected on this site. |
| `tech_count` | How many technologies were detected. |
| `server_header` | The raw `Server` response header, or `null` if the site didn't send one. |
| `robots_allowed` | Whether this site's robots.txt allowed fetching its home page for `User-agent: *`. |
| `fetched_at` | UTC timestamp of the fetch. |
| `error` | `null` on a normal fetch; otherwise the honest reason no technologies could be read (e.g. `"HTTP 404"`, a robots.txt block, or a network failure). |

### Pricing

**$0.004 per site row. No start fee, pay per result.**

A run of 100 sites costs $0.40. A site that could not be reached still
returns one row (with `error` set) and is billed the same as a successful
one, because it is still one result for the address you asked about.

### Example — a real run, 2026-09-21

Input:

```json
{"urls": ["https://www.allbirds.com/", "https://wordpress.org/", "https://stripe.com/"], "maxItems": 10}
```

Output (row 1 of 3, from a live local run — see `LOCAL_RUN.md`):

```json
{
  "site": "https://www.allbirds.com/",
  "final_url": "https://www.allbirds.com/",
  "http_status": 200,
  "technologies": [
    {"name": "Cloudflare", "category": "CDN/hosting", "confidence": "high", "evidence": "cf-cache-status: DYNAMIC"},
    {"name": "Attentive", "category": "email/marketing", "confidence": "high", "evidence": "cdn.attn.tv"},
    {"name": "Shop Pay", "category": "payments", "confidence": "medium", "evidence": "shop.app"},
    {"name": "Shopify", "category": "shop platform", "confidence": "high", "evidence": "cdn.shopify.com"},
    {"name": "Google Tag Manager", "category": "tag manager", "confidence": "high", "evidence": "googletagmanager.com/gtm.js"}
  ],
  "categories_found": ["CDN/hosting", "email/marketing", "payments", "shop platform", "tag manager"],
  "tech_count": 5,
  "server_header": "cloudflare",
  "robots_allowed": true,
  "fetched_at": "2026-09-21T17:23:04Z",
  "error": null
}
```

The other two rows from that same run: `wordpress.org` came back with
`WordPress` and `Google Tag Manager` (2 technologies); `stripe.com` came
back with `Next.js` (1 technology). Full rows are in `LOCAL_RUN.md`.

### Limitations — what this does not do

- **Reads the home page only, once.** Each site is fetched exactly once,
  the home page, on purpose. It does not crawl `/about`, `/pricing`, or
  any other page, and it does not cache or diff between runs.
- **No JavaScript execution.** Detection reads the first HTTP response's
  headers, cookie names, meta tags, and HTML text. A technology that is
  injected only by client-side JavaScript after page load, with no static
  marker in that first response, can be missed. This is a plain HTTPS
  fetch, not a browser.
- **Cookie names only, never cookie values.** No login, no session,
  no personal data of any kind is read or stored.
- **Not exhaustive.** 134 technologies are covered; a real site can run
  something this rules file does not yet recognise, and that technology
  will simply be absent from `technologies` rather than guessed at.
- **No speed or uptime claims.** This README states measured counts from
  the runs shown above and makes no promise about how fast or how
  reliably any individual target site will respond.
- **Respects robots.txt.** A site whose robots.txt disallows `User-agent: *`
  fetching its home page is never fetched; that row reports
  `robots_allowed: false` and an explanatory `error` instead.
- When the proxy setting is set, every request goes through that proxy.

### FAQ

**Where do the detection rules come from?**
Written by this Actor's author from public knowledge of each product's own
documented script hosts, cookie-name conventions, generator tags, and
response headers (`data/fingerprints.json`). Not copied from any
third-party fingerprint database.

**Does it log in to the target sites?**
No. Every fetch is a single plain, signed-out HTTPS `GET`, exactly what a
visitor with no account would receive.

**What happens if a site blocks the request or is down?**
A site that could not be reached at all gets `http_status: null`, a
plain-English `error`, and an empty `technologies` list. A site that
answers with an error status instead (e.g. a 404 page) keeps that status
code and `error`, and still reports any technology found on that actual
error page — nothing is invented either way, and the row still bills
once, because it is still your one result for that address.

**Is there support?**
Yes. Raise it through the Apify Store issues tab for this actor, or email
operations@ustechautomations.com. Replies within 2 business days.

### Refunds and support

Refunds: if a run returns zero rows for a valid input, email
operations@ustechautomations.com within 7 days and we refund that run.
Support: same address, replies within 2 business days.

# Actor input Schema

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

One company website per item, e.g. https://example.com. A bare domain (example.com) is treated as https://example.com.

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

Hard ceiling on rows returned, and therefore on the cost of the run. Every row bills at the listed price from row one; there is no start fee.

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

Optional Apify proxy. Leave off for a local run.

## Actor input object example

```json
{
  "urls": [
    "https://www.allbirds.com/",
    "https://wordpress.org/"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `sites` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `report` (type: `string`):

No description

# 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": [
        "https://www.allbirds.com/",
        "https://wordpress.org/"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("usta/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": [
        "https://www.allbirds.com/",
        "https://wordpress.org/",
    ],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("usta/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": [
    "https://www.allbirds.com/",
    "https://wordpress.org/"
  ],
  "maxItems": 50
}' |
apify call usta/tech-stack-detector --silent --output-dataset

```

## MCP server setup

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