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

Find what any list of websites runs (CMS, ecommerce, analytics, marketing, chat, payments, CDN, hosting) with evidence, plus email provider, email senders and verified SaaS from public DNS, and each site's AI-crawler policy. Pay per website read. Blocked sites are free.

- **URL**: https://apify.com/gastings/tech-stack-detector.md
- **Developed by:** [Oleh Hastov](https://apify.com/gastings) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 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.

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

**Give it a list of websites and get back what each one runs (CMS, ecommerce platform, analytics, marketing and chat tools, payments, CDN, hosting) with evidence for every detection. Each row also carries what a homepage cannot show: the company's email provider, the services that send its email and the SaaS tools it has verified its domain with, read from public DNS, plus how the site treats AI crawlers. Pay per website read. Blocked and unreachable sites are free.**

Built for sales and lead-generation teams who segment prospects by stack ("Shopify stores using Klaviyo", "Microsoft 365 companies that use Zendesk"), agencies qualifying leads, market and competitor research, and AI agents that need to enrich a domain in one call.

### What you get for each website

| Field group | Read from | Examples |
|---|---|---|
| Technologies | homepage headers, cookies, meta tags, script URLs, inline loader snippets, HTML, DNS (NS, CNAME) | WordPress 6.6, Shopify, Next.js 15, Google Tag Manager, HubSpot, Intercom, Stripe, Cloudflare, Vercel |
| Ready-made columns | the technologies above, grouped (frameworks such as Next.js go in `js_frameworks`, not `cms`) | `cms`, `ecommerce`, `js_frameworks`, `analytics`, `tag_managers`, `advertising`, `marketing_automation`, `crm`, `live_chat`, `payments`, `cdn`, `hosting`, `web_servers`, `consent`, `ab_testing`, `reviews`, `security`, `ai_tools` |
| Email | DNS MX, SPF, DMARC | provider (Google Workspace, Microsoft 365, ...), security gateway (Proofpoint, Mimecast, ...), sending services (Salesforce, Zendesk, HubSpot, SendGrid, Mailchimp, ...), DMARC policy |
| Verified SaaS | DNS TXT verification records | Atlassian, Slack, Zoom, Notion, Figma, DocuSign, Stripe, OpenAI, Anthropic, Cursor and 150 more vendors |
| AI-crawler policy | robots.txt, llms.txt, /.well-known/tdmrep.json, robots meta | verdict for 18 named AI bots (GPTBot, ClaudeBot, Google-Extended, PerplexityBot, ...) grouped as training, search and user-triggered; how many of 200+ known AI agents are blocked; Cloudflare Content-Signal; IETF Content-Usage; llms.txt; TDM reservation; noai |
| Evidence | per technology | confidence 0 to 100 and up to three short reasons, such as `script https://js.stripe.com/v3/` or `DNS NS ns-12.awsdns-01.com` |

Every input produces exactly one row with `status` = `ok` (homepage read), `blocked` (the site refused the visit), `not_found` (no such website) or `error` (timeout, server error, invalid input, or not processed because your maximum total charge was reached). `example.com` and `www.example.com` count as one website. A run never "succeeds" with an empty dataset: if nothing could be read, the run fails and says why.

### Why this one

- **DNS signals that page-only tools miss.** In a sample of 200 well-known UK and US company domains read on 1 Nov 2026, 70% had a Microsoft 365 verification record, 59% Atlassian, 41% Anthropic, 38% OpenAI and 22% Cursor. None of that is visible on a homepage.
- **Evidence and a confidence for every detection.** Weak hints (a domain that is only allowed in a Content-Security-Policy header) score 40 and stay hidden at the default threshold of 50; set `minConfidence` to 0 to see them.
- **Honest about blocks.** When a site refuses the visit, the row says who refused it (Cloudflare, Akamai, Imperva, DataDome and others), keeps the DNS and robots.txt findings, and costs nothing.
- **About 7,700 technology fingerprints**, plus our own rules for chat and marketing tools whose standard install snippet only names its loader URL (Intercom, Drift, Microsoft Clarity, Mixpanel, Pardot, Gorgias and others).
- **Fair pricing.** $3 per 1,000 websites read. Blocked, not-found and error rows are free, there is no minimum run cost, and the DNS and AI-crawler checks are included.
- **Agent-ready.** List input, typed JSON output with a documented schema, ten websites in parallel by default, works from the Apify MCP server.

### Input

```json
{
  "domains": ["octopus.energy", "https://www.thortful.com/", "jane@pukkaherbs.com"],
  "checkDns": true,
  "checkAiPolicy": true,
  "includeEvidence": true,
  "minConfidence": 50
}
```

- `domains`: domains, URLs or email addresses (their domain is used). Duplicates are removed.
- `checkDns`: email provider, email senders, DMARC and verified SaaS from public DNS.
- `checkAiPolicy`: robots.txt verdicts for AI crawlers, llms.txt, TDM reservation and noai tags.
- `includeEvidence`: up to three reasons per technology; switch off for smaller rows.
- `minConfidence`: 0 to 100, default 50.
- `respectRobotsTxt` (default off): skip homepages whose robots.txt disallows all visitors; those rows are free.
- `maxConcurrency`: websites checked at the same time (1 to 50, default 10).

### Output example

One real row, read from octopus.energy on 1 Nov 2026 and shortened (the full row lists 11 technologies and 22 verified services). The email provider sits behind a Mimecast gateway, so the MX records alone would only show Mimecast; the SPF record names Google Workspace.

```json
{
  "status": "ok",
  "domain": "octopus.energy",
  "final_url": "https://octopus.energy/",
  "title": "Octopus Energy: The UK's most awarded energy supplier",
  "js_frameworks": ["Next.js", "React"],
  "hosting": ["Amazon Route 53", "Amazon Web Services", "Vercel"],
  "reviews": ["Trustpilot"],
  "email_provider": "Google Workspace",
  "email_security_gateway": "Mimecast",
  "email_senders": ["SendGrid", "Google Workspace", "Mimecast", "HubSpot"],
  "dmarc_policy": "reject",
  "verified_service_names": ["Microsoft 365", "Zoom", "Anthropic", "Atlassian", "Figma", "HubSpot", "OpenAI", "Slack", "Stripe"],
  "ai_policy_summary": "allows_ai",
  "llms_txt": false,
  "email": {
    "provider": "Google Workspace",
    "provider_evidence": "SPF include:_spf.google.com (MX is Mimecast)",
    "mx_hosts": ["eu-smtp-inbound-1.mimecast.com", "eu-smtp-inbound-2.mimecast.com"]
  },
  "technologies": [
    {"name": "Next.js", "categories": ["JavaScript frameworks", "Web frameworks", "Web servers", "Static site generator"],
     "version": null, "confidence": 100, "evidence": ["script https://octopus.energy/_next/static/immutable/chunks/26827qemr2pt4.js"]},
    {"name": "Vercel", "categories": ["PaaS"], "version": null, "confidence": 100,
     "evidence": ["header server: Vercel", "header x-vercel-cache: HIT"]},
    {"name": "Trustpilot", "categories": ["Reviews"], "version": null, "confidence": 75,
     "evidence": ["inline script loads //widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js"]}
  ]
}
```

The dataset has four table views: **Tech stack**, **Email & SaaS (DNS)**, **AI-crawler policy** and **All technologies**. Export as JSON, CSV or Excel, or read it through the API.

### Pricing

| Event | Price | When |
|---|---|---|
| Website | $0.003 ($3 per 1,000) | the homepage was read and analysed |
| Actor start | $0.00005 per GB of run memory | once per run: $0.0002 at the default 4 GB |

Blocked, not-found and error rows are not charged. Set a maximum total charge on the run and the actor never goes over it: each website reserves its charge before it starts, and inputs beyond the limit still get a row (status `error`, category `budget`) at no cost.

### Limits

- **HTTP only, homepage only.** Technologies that show themselves only through JavaScript running in a browser (about a fifth of the fingerprint set) are missed unless their install snippet names a loader URL we recognise. Inner pages are not visited.
- **Some sites refuse automated visits.** Those rows are marked `blocked`, name the protection when it can be recognised, still carry DNS and robots.txt findings, and are free. No proxies or CAPTCHA tricks are used.
- **DNS signals are read for the domain you give** (without `www.`). For a subdomain such as `shop.example.com`, also give the company's main domain to get its email and SaaS signals; platform domains (`myshopify.com`, `github.io`, ...) are never used as a stand-in.
- **DNS lookups use the platform's resolver first.** When it cannot answer (some networks drop large answers), the same question goes to public resolvers (Cloudflare 1.1.1.1, Google 8.8.8.8). A lookup that still fails leaves the DNS fields empty; it never reads as "no records".
- **A DNS verification record shows that the organisation set up an account with a vendor at some point.** It does not prove the tool is in use today.
- **Unusual pages are handled defensively.** Fingerprint checks have time limits; when one is cut short the row carries `analysis_limited: true`. When a site's HTTPS certificate fails, the page is read without verification (read-only) and `tls_valid` is false. Domains that point to private or internal addresses are not fetched.
- **The AI-crawler policy reports what the site states** (robots.txt, llms.txt, TDMRep, tags). It does not measure whether crawlers comply. When robots.txt cannot be fetched, the summary is `unknown`.

### Data sources and licences

Technology detection uses the community fingerprint set enthec/webappanalyzer (GPL-3.0), run here as a hosted service. AI crawler names come from the ai.robots.txt project (MIT). Bot purposes come from each vendor's own documentation. This actor is not affiliated with BuiltWith or Wappalyzer.

### Support

Open an issue on this page; issues are answered within 12 hours on working days. Feature requests are welcome: a "changes since last run" monitor (technology added or removed, new verified SaaS) and inner-page scanning are on the list.

# Actor input Schema

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

Domains or URLs, one per line (example.com, https://www.example.co.uk/page, or an email address: its domain is used). Duplicates are removed. Each website gets one row.

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

Reads public MX, TXT and DMARC records: email provider (Google Workspace, Microsoft 365, ...), email security gateway, services that send email for the domain (Salesforce, Zendesk, HubSpot, SendGrid, ...), DMARC policy, and SaaS vendors the organisation verified its domain with (Atlassian, Slack, Zoom, OpenAI, Anthropic, ...). Also NS and CNAME records for DNS host and CDN detection. Included in the price.

## `checkAiPolicy` (type: `boolean`):

Reads robots.txt, llms.txt and /.well-known/tdmrep.json: which AI crawlers (GPTBot, ClaudeBot, Google-Extended, PerplexityBot and more) the site blocks, Cloudflare Content-Signal, llms.txt presence, TDM reservation, noai tags. Included in the price.

## `includeEvidence` (type: `boolean`):

Up to three short reasons per technology (the header, cookie, script URL or DNS record that matched). Turn off for smaller rows.

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

Technologies below this confidence (0 to 100) are left out. 50 hides weak hints such as a domain that is only allowed in the Content-Security-Policy header; 0 shows everything.

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

When on, a site whose robots.txt disallows '/' for all user agents is not visited; its row is marked blocked (free) and still carries DNS and AI-policy data.

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

How many websites are checked at the same time.

## Actor input object example

```json
{
  "domains": [
    "octopus.energy",
    "thortful.com",
    "wordpress.org"
  ],
  "checkDns": true,
  "checkAiPolicy": true,
  "includeEvidence": true,
  "minConfidence": 50,
  "respectRobotsTxt": false,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

All rows: technologies with categories, versions, confidence and evidence; ready-made category columns; email provider, senders and verified SaaS from DNS; AI-crawler policy. Opens on the Tech stack table; the other views and JSON, CSV and Excel exports are one click away.

## `runSummary` (type: `string`):

Counters for the run: inputs, rows by status (ok, blocked, not found, error), requests, errors by category, seconds.

# 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": [
        "octopus.energy",
        "thortful.com",
        "wordpress.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gastings/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": [
        "octopus.energy",
        "thortful.com",
        "wordpress.org",
    ] }

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

```

## MCP server setup

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