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

Tech stack detector and website technology lookup for site lists: CMS, e-commerce, JS frameworks, analytics, CDN, hosting, payments. 480+ technologies with evidence. $7/1,000 sites; none found = free.

- **URL**: https://apify.com/tidytools/website-tech-stack-detector.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (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 $5.60 / 1,000 analyzed 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?

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

### What does Tech Stack Detector do?

Give it a list of websites (bare domains like `stripe.com` or full URLs) and get the **technologies each site runs**, with the evidence for every detection:

- 🧱 **CMS and site builders**: WordPress, Drupal, Joomla, Ghost, Wix, Squarespace, Webflow, HubSpot CMS, Framer, Duda, Weebly, headless CMSs (Contentful, Sanity, Storyblok, Prismic)…
- 🛒 **E-commerce**: Shopify, WooCommerce, Magento, BigCommerce, PrestaShop, Salesforce Commerce Cloud, Ecwid…
- ⚛️ **JavaScript frameworks and libraries**: Next.js, Nuxt, React, Vue.js, Angular, Svelte/SvelteKit, Astro, Gatsby, Remix, jQuery (with version), Bootstrap, Tailwind CSS…
- 📊 **Analytics, tag managers and ads**: Google Analytics, Google Tag Manager, Hotjar, Microsoft Clarity, Mixpanel, Segment, Meta Pixel, LinkedIn Insight, TikTok Pixel, Google Ads…
- ☁️ **CDN, hosting and web servers**: Cloudflare, Fastly, Akamai, CloudFront, Vercel, Netlify, Heroku, WP Engine, Kinsta, Nginx (with version), Apache, LiteSpeed…
- 💳 **Payments, chat, marketing automation, A/B testing, cookie consent, bot protection, fonts, maps and video**: Stripe, PayPal, Klarna, Intercom, Zendesk, HubSpot, Klaviyo, Optimizely, OneTrust, Cookiebot, reCAPTCHA, DataDome, Google Fonts, YouTube…
- ✉️ **E-mail provider and SaaS seen in DNS** (new): the MX provider (Google Workspace, Microsoft 365, Proofpoint, Mimecast…), senders from SPF and DKIM (SendGrid, Mailchimp, Amazon SES, Postmark, HubSpot, Marketo, Salesforce…) and the domain-verification records companies add for Atlassian, Zoom, DocuSign, Slack, Apple, Meta, OpenAI, Notion and 80+ other services.

**480+ technologies in 41 categories from the page, plus 130+ services seen in public DNS.** Each one comes with a `confidence` (`high` or `medium`), a `version` when the site reveals it, and up to 3 pieces of `evidence` (the header, cookie, generator tag, script URL or HTML snippet that matched; `dns: …` for DNS findings).

#### How detection works

The home page (plus optional extra pages) is fetched from Apify's network so the **response headers and cookies** can be read too: they reveal the server, CDN, hosting and bot protection. The Actor then checks our own hand-written fingerprint list against:

- response headers (`server`, `x-powered-by`, `cf-ray`, `x-vercel-id`, `powered-by: Shopify`…) and Set-Cookie names;
- `<meta name="generator">` and other meta tags;
- script, stylesheet and iframe URLs, including loader URLs inside inline snippets (e.g. the Google Tag Manager snippet);
- markers in tag attributes (`id="__NEXT_DATA__"`, `ng-version`, `data-reactroot`, `data-wf-page`…), inline JavaScript (`window.Shopify`, `fbq('init'`) and HTML comments (Yoast SEO).

**Precision first:** visible page text, ordinary links and code samples are never searched, so an article that *mentions* WordPress or Shopify does not count. A single image hot-linked from a platform's CDN only gives `medium` confidence. Set **Minimum confidence** to *High only* to keep just the strongest evidence.

**Public DNS (on by default, free):** the Actor also reads the domain's MX, SPF, DKIM, TXT and name-server records over DNS-over-HTTPS. Tools found only there are listed with `source: "dns"` and evidence such as `dns: MX aspmx.l.google.com`, `dns: SPF include:sendgrid.net` or `dns: TXT atlassian-domain-verification=…` (the token itself is never output). A tool found on the page and in DNS gets `alsoInDns: true`. Each row also has `emailProvider` and a `dns` object (`mxProvider`, `mxHosts`, `dnsHost`, `spfIncludes`, `emailAuth` with SPF and DMARC policies). For a subdomain such as `blog.acme.com`, the parent domain's mail records are used. A verification record shows the company *has an account* with that service; it does not prove the service is used on this website. Turn it off with **Check public DNS records**.

### Use cases

- **Lead generation and sales prospecting**: find Shopify or WooCommerce stores, sites without analytics, or companies using a competitor's tool
- **Competitor research**: see what analytics, A/B testing, chat and marketing tools other sites use
- **Agencies**: qualify prospects before a pitch (old jQuery, no cookie consent, which CMS)
- **Security and IT inventory**: servers, CDNs and bot protection in front of a list of domains
- **Data enrichment**: add a tech-stack column to a lead list from another Actor (dataset input)

### How much does it cost?

| Event | Price |
|---|---|
| Analyzed website | **$7.00 / 1,000 websites** ($0.007 each) |

**Example:** 1,000 websites, of which 900 have at least one detected technology, cost 900 × $0.007 = **$6.30**.

**No start fee.** Higher Apify plans get volume discounts (see the *Pricing* tab). Extra pages per site are included in the site price. **You pay only for sites where at least one technology is detected.** Sites with no technology detected, and websites that are unreachable, blocked or return an error, are **not charged**; failed sites carry an `errorType` (`blocked`, `network`, `timeout`, `not_found`…). Every row has `charged: true` or `false`.

#### Control your cost

- **Before anything is read**, the run logs its plan: the number of sites × the price = the most the run can cost, compared with your maximum charge per run.
- **Charged**: a site with at least one technology detected ($0.007).
- **Free**: sites with no technology detected, unreachable or blocked sites, lines that are not a website (e.g. "Acme Inc"), and duplicates: `wordpress.org`, `www.wordpress.org` and `https://wordpress.org/` are one site, and two inputs that redirect to the same final page are analyzed and charged once (the second row has `duplicateOf` and `charged: false`).
- **Maximum charge per run**: set it in the run options (Console) or with `maxTotalChargeUsd` (API); **Max sites per run** is a second cap. When the next site could exceed the charge limit, no new site is started; the run ends with status `LIMIT_REACHED`, and the `SUMMARY` record lists the sites not processed (`notProcessed`: the count and up to 100 inputs) so you can run them again with a higher limit.
- **Unexpected runs?** This Actor only runs when you, a schedule, a saved task or an integration (API, Make, n8n, Zapier) starts it. Each run in the Console shows how it was started; check **Schedules** and **Integrations** if you see runs you did not expect.
- **If Apify restarts the run** (server migration or Resurrect), items already finished are skipped and not charged again (`SUMMARY.resumedSkipped`).

#### Price comparison (checked 2 October 2026)

| Actor | Price (free plan) | Notes from the listing |
|---|---|---|
| **Website Tech Stack Detector (this Actor)** | **$0.007 per website with at least one detection**, no start fee | Zero-detection, unreachable and duplicate sites are free; evidence, confidence and version per technology; e-mail provider and SaaS from DNS |
| nexgendata/wappalyzer-replacement | $0.10 per domain ("tech-detection" event), no start fee | 1,232 users in the last 30 days |
| builtwith/builtwith-official-technology-scraper | $0.002 per dataset result | Data from builtwith.com |
| automation-lab/tech-stack-detector | $0.0023 per URL + $0.035 per run start | Cheaper than this Actor for large lists |
| automation-lab/wappalyzer-technology-lookup | $0.00024 per website + $0.005 per run start | Cheaper than this Actor for large lists |
| scrapemint/website-tech-stack-detector | $0.01 per website with at least one detection | Zero-detection and unreachable sites are free; first row per run free |
| oneary/builtwith-domain-scraper | $0.025 per result | Finds sites using a given technology (reverse lookup) |

Prices are from the Apify Store listings (free-plan prices; most Actors, including this one, are cheaper on higher Apify plans). automation-lab's per-site prices and the BuiltWith Actor are lower than ours; we compete on hand-checked precision, evidence for every detection, version numbers, header-based detection (CDN, hosting, servers), DNS findings and no start fee.

### Switching from another tech stack detector

**Paste your existing input as-is.** Besides our own fields (`urls`, `urlsText`, `startUrls`), the Actor reads the website fields other tech stack detectors use: `domains`, `websites`, `targetUrls`, `startDomains`, `sites` and a single `url` (text with several sites separated by commas or new lines works). `timeout_seconds` is read as **Timeout per page**, `maxRequestsPerCrawl` and `maxItems` as **Max sites per run**, `concurrency` as **Parallel sites** and `proxy` as **Proxy**. `include_versions` and `include_confidence` are not needed (versions and confidence are always included), and `categories_filter` is not applied (all categories are returned). The run log lists every field it mapped or ignored. When both our field and an alias are set, ours wins; sites from several fields are merged and de-duplicated.

#### Switching from nexgendata/wappalyzer-replacement

| | nexgendata/wappalyzer-replacement | This Actor |
|---|---|---|
| Price per 1,000 sites (free plan) | $100 ($0.10 per domain) | **$7** ($0.007 per site with a detection) |
| Start fee | None | None |
| Sites with no detection or that fail | Not stated per site in the listing ("a run that delivers nothing bills no result events") | **Free** (`charged: false`, with an `errorType`) |
| Technologies | 251 fingerprints (listing) | 480+ in 41 categories, plus 130+ services from public DNS |
| Evidence, version, confidence | Yes (confidence 0–100) | Yes (up to 3 pieces of evidence; confidence `high` / `medium`) |
| E-mail provider and SaaS from DNS (MX, SPF, DKIM, TXT) | Not in the listing | Yes, on by default, free |
| Blocked sites | Not in the listing | Falls back to our servers, then a real browser |
| Category filter | Yes (`categories_filter`) | No; filter the `categories` field or overview columns |
| Lead lists from another Actor (dataset input) | Not in the listing | Yes, with fields kept per row |

Your input works unchanged:

```json
{
  "urls": ["https://stripe.com", "https://shopify.com"],
  "include_confidence": true,
  "include_versions": true,
  "timeout_seconds": 15
}
```

Output fields with a different name: `tech_names` → `technologyNames`, `tech_count` → `technologyCount`, `status_code` → `httpStatus`, `final_url` → `finalUrl`; `categories` (category → names) and `technologies[]` (`name`, `category`, `version`, `evidence`, `confidence`) keep their names, with `evidence` as a list and `confidence` as `high` or `medium` instead of a number.

#### Switching from builtwith/builtwith-official-technology-scraper

| | builtwith/builtwith-official-technology-scraper | This Actor |
|---|---|---|
| Price per 1,000 sites (free plan) | **$2** ($0.002 per result) | $7 ($0.007 per site with a detection) |
| Start fee | None | None |
| Sites with no detection or that fail | Not stated in the listing | **Free** |
| Where the data comes from | BuiltWith's technology profiles | A live check of the page you give (headers, cookies, HTML, scripts) and its public DNS today |
| Input | Root domains only | Domains, full URLs, subdomains, extra pages per site, datasets |
| Evidence and version per technology | Not in the listing (name, tag, categories, link) | Yes |

The BuiltWith Actor is cheaper and draws on BuiltWith's own data; choose this Actor when you need to see what a site runs right now, with the evidence for each detection, or need specific pages, subdomains or DNS findings. Your input works unchanged:

```json
{
  "startDomains": ["builtwith.com", "airbnb.com"],
  "maxRequestsPerCrawl": 10000000
}
```

(`maxRequestsPerCrawl` becomes **Max sites per run**, capped at 10,000.) Output: `domain` → `site`, `techs[].name` → `technologies[].name`, `techs[].categories` → `technologies[].category`.

### How to use it

1. Paste your **websites** (one per line), paste a column of domains as text, or pick a **dataset** from another Actor run and name the field that holds the website. Blank lines are ignored; a line that is not a website gets one `invalid_input` row (not charged).
2. Optional: add **Extra pages per site** such as `/pricing` or `/cart` (up to 5), since some tools only load on certain pages.
3. Click **Start** and export the results as JSON, CSV or Excel, or call the Actor from the API.

The **overview** table shows one row per site with `technologyNames` and shortcut columns (`cms`, `ecommerce`, `jsFramework`, `analytics`, `hosting`). The **details** view and the JSON have the full `technologies` list.

#### Output example (real result, September 2026)

```json
{
    "input": "wordpress.org",
    "inputIndex": 0,
    "url": "https://wordpress.org",
    "site": "wordpress.org",
    "finalUrl": "https://wordpress.org/",
    "httpStatus": 200,
    "success": true,
    "technologyCount": 5,
    "technologyNames": "WordPress 7.2, Google Tag Manager, Nginx, PHP, Jetpack",
    "cms": "WordPress",
    "ecommerce": null,
    "jsFramework": null,
    "analytics": "Google Tag Manager",
    "hosting": null,
    "technologies": [
        {
            "name": "WordPress",
            "category": "CMS",
            "confidence": "high",
            "version": "7.2",
            "evidence": [
                "header: link: <https://wordpress.org/wp-json/>; rel=\"https://api.w.org/\", <https://wordpress.…",
                "meta: generator: WordPress 7.2-alpha-63999",
                "script: https://wordpress.org/wp-content/mu-plugins/pub-sync/blocks/language-suggest/build/front.js?ver=5f3f8a2de66964d2bf04"
            ]
        },
        { "name": "Google Tag Manager", "category": "Tag manager", "confidence": "high", "version": null,
          "evidence": ["iframe: https://www.googletagmanager.com/ns.html?id=GTM-P24PF4B", "..."] },
        { "name": "Nginx", "category": "Web server", "confidence": "high", "version": null, "evidence": ["header: server: nginx"] },
        { "name": "PHP", "category": "Programming language", "confidence": "high", "version": null, "evidence": ["implied by WordPress"], "impliedBy": "WordPress" },
        { "name": "Jetpack", "category": "WordPress plugin", "confidence": "high", "version": null, "evidence": ["script: https://stats.wp.com/e-202640.js"] }
    ],
    "categories": { "CMS": ["WordPress"], "Tag manager": ["Google Tag Manager"], "Web server": ["Nginx"], "Programming language": ["PHP"], "WordPress plugin": ["Jetpack"] },
    "pagesChecked": ["https://wordpress.org/"],
    "signals": "full",
    "via": "direct",
    "charged": true
}
```

More real results from the same test runs (`technologyNames`):

| Site | Detected |
|---|---|
| allbirds.com | Shopify, Vue.js, Tailwind CSS, GSAP, Swiper, Google Tag Manager, Cloudflare, Shop Pay, OneTrust |
| gymshark.com | Contentful, Shopify, Next.js, React, Amazon CloudFront |
| vercel.com | Next.js, React, Tailwind CSS, Vercel |
| techcrunch.com | WordPress 6.9.9, Google Tag Manager, Nginx, PHP, Cloudflare Turnstile, reCAPTCHA, Jetpack, Yoast SEO 25.1 |
| python.org | jQuery 1.8.2, jQuery UI, Modernizr, Fastly, Google Hosted Libraries, Nginx, Varnish |
| paigebrunton.com (Squarespace site) | Squarespace, jQuery 3.5.1, Google Analytics, Google AdSense, Meta Pixel, Adobe Fonts, Google Fonts |
| wix.com | Wix, React, core-js, Lodash, Sentry, Google Sign-In |
| bbc.com | Next.js, React, Fastly, Varnish, Optimizely |

- `confidence`: `high` = a specific header, cookie, generator tag, script URL or markup marker; `medium` = only indirect hints (an image on a platform's CDN, a CSS class pattern).
- `impliedBy`: added because another detection implies it (Next.js → React, WooCommerce → WordPress).
- `signals`: `full` (HTML + headers), or `assets` / `assets+headers` when the site blocked Apify's network and only the page's script, stylesheet and iframe URLs could be read through our servers (fewer detections; `fallbackReason` says why).
- `via`: `direct` (Apify's network), `backend` (our servers) or `browser`.
- `input` is the line you gave (`inputs` lists every line merged into the same site) and `inputIndex` its position; rows from a dataset also have `datasetItemIndex`, and **Dataset fields to keep** (e.g. `title`, `placeId`) adds a `source` object with those fields of the dataset item.
- **Google Maps lists**: every dataset item gets one row. Places without a website (`errorType: "no_website"`) and places whose "website" is an Instagram, Facebook, Yelp or similar page (`errorType: "not_a_website"`; analyzing it would report the platform's stack, not the business's) are not charged. In our test with 30 Maps places, 24 sites were analyzed and charged ($0.168, 7 seconds), mostly WordPress, Squarespace, Webflow and Shopify.
- **Subdomains are analyzed as given**: `shop.brand.com` or `blog.brand.com` is analyzed itself, not the parent domain `brand.com`.
- Failed sites have `success: false`, an `error` and an `errorType`; if the block page itself revealed something (e.g. DataDome, Cloudflare), it is listed in `headerHints`.
- The `SUMMARY` record in the key-value store has the run `status` (`SUCCESS`, `PARTIAL_RESULTS`, `FAILED`, `NO_RESULTS`, `LIMIT_REACHED`), failures by `errorType`, charged and free sites, merged duplicates and the 30 most common technologies across your list.

### Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/website-tech-stack-detector) to Claude, Cursor or any MCP client, then ask e.g. "Which of these 50 domains run Shopify, and which analytics tools do they use?"

```json
{ "urls": ["stripe.com", "allbirds.com"] }
```

Failed sites and sites with no technology detected are not charged; failed sites carry an `errorType`.

### Use it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/tidytools~website-tech-stack-detector/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls":["stripe.com","allbirds.com"]}'
```

Send websites in `urls` (plain strings; bare domains work). The URL-list field `startUrls` is also accepted, but Apify rejects the whole run (HTTP 400) when it holds a bare domain or a blank line. Input field names from other tech stack detectors (`domains`, `websites`, `startDomains`...) also work; see *Switching from another tech stack detector*.

**Schedules and integrations:** run it daily or weekly with Apify Schedules, get a webhook when a run finishes, or send the results to Zapier, Make, n8n, Google Sheets, Slack and other apps with Apify integrations. Results can be exported as JSON, CSV, Excel or XML.

### Advanced settings

- **Plain HTTP requests from**: *Auto* (recommended) fetches from Apify's network first (headers and cookies are needed for CDN, hosting and server detection). If a site blocks that request, it reads the page's script and stylesheet URLs through our servers, with a real browser as the last step. *Our servers only* skips headers. *Apify's network only* never falls back.
- **Proxy**: optional Apify Proxy for requests sent from Apify's network (billed to your Apify account). Useful for sites that block data-center IPs.
- **Max sites per run**, **Timeout per page** and **Parallel sites** control cost and speed.
- **Check public DNS records** (default on): adds the e-mail provider and services from DNS. A site where only its DNS host was found (e.g. a parked domain) is not charged.

### Limitations

- Detection reads the HTML the server sends (it does not run the page's JavaScript). Tools injected later by a tag manager, or only on other pages, can be missed; add them via **Extra pages per site**. Tools loaded through Google Tag Manager show up as Google Tag Manager.
- Sites behind strong bot protection (e.g. nytimes.com with DataDome in our tests, or drupal.org's JavaScript challenge) may fail or give only header-based results. A proxy can help.
- DNS findings show accounts the company verified or mail senders it authorized; they can be older than the website or belong to another team in the company.
- The fingerprint list is our own and covers 480+ technologies plus 130+ DNS services; it is smaller than the largest commercial databases. Missing a technology you need? Open an issue.

### FAQ

**Is this a BuiltWith or Wappalyzer alternative?** It detects the technologies each website runs using our own hand-written fingerprint list of 480+ technologies in 41 categories (plus 130+ services from public DNS records, like BuiltWith's e-mail and verification data), with a `confidence`, a `version` when the site reveals it and up to 3 pieces of `evidence` per detection. The list is smaller than the largest commercial databases; the price comparison table lists BuiltWith- and Wappalyzer-based Actors on Apify.

**Can I use it as a CMS detector (WordPress, Shopify, Wix...)?** Yes. CMSs, site builders and e-commerce platforms such as WordPress, Wix, Squarespace, Webflow, Shopify and WooCommerce are detected, and the overview table has `cms` and `ecommerce` columns. Set **Minimum confidence** to *High only* to keep just the strongest evidence.

**Does the website technology detector see tools loaded by JavaScript?** Detection reads the HTML the server sends and does not run the page's JavaScript, so tools injected later by a tag manager, or only loaded on other pages, can be missed. Add those pages (e.g. `/pricing`, up to 5) with **Extra pages per site**; tools loaded through Google Tag Manager show up as Google Tag Manager.

**Can I detect the tech stack of a Google Maps or lead list?** Yes. Set **Websites from another Actor's dataset (dataset ID)** and **Dataset field with the website**; every dataset item gets one row. Places without a website or whose website is an Instagram, Facebook or Yelp page are not charged, and you pay only for sites where at least one technology is detected.

### Support

Open an issue in the **Issues** tab with the website and the technology you expected. Issues are checked regularly.

Found this useful? A short review on the Store helps other users find it.

# Actor input Schema

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

Main input (fill this, or `urlsText` / `startUrls` / `datasetId`). One website per line: bare domains (stripe.com) or full URLs. Each site's home page (or the exact URL you give) is analyzed; subdomains such as shop.brand.com are analyzed as given, not as the parent domain. stripe.com, www.stripe.com and https://stripe.com/ count as one site (analyzed and charged once). Lines that are not a domain are reported as invalid and not charged. Up to 10,000 per run.

## `urlsText` (type: `string`):

Optional: paste domains or URLs separated by new lines, commas, semicolons, tabs or spaces (e.g. a column copied from a spreadsheet).

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

The same as "Websites", as a URL list; you can also upload or link a text/CSV file of websites. Bare domains belong in the list above (this editor rejects them).

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

Optional: analyze the websites in a dataset from another run (e.g. a Google Maps or lead list scrape).

## `datasetField` (type: `string`):

Field that holds the website or URL, e.g. website or url (dot paths like company.website work). Leave empty to guess.

## `datasetKeepFields` (type: `array`):

Fields copied from each dataset item into the output's "source" object, e.g. title, placeId, so every row maps back to the record it came from (for example a Google Maps place).

## `extraPages` (type: `array`):

Optional: paths also checked on every site, e.g. /pricing or /blog (at most 5). Some tools only load on certain pages (checkout, blog, contact). Included in the site price.

## `minConfidence` (type: `string`):

High = a specific header, cookie, generator tag or script URL was found. Medium = only indirect hints (e.g. an image hosted on a platform's CDN or a CSS class pattern).

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

Add up to 3 pieces of evidence per technology (the header, cookie, meta tag, script URL or HTML snippet that matched).

## `includeDns` (type: `boolean`):

Also read the domain's public DNS (MX, SPF, DKIM, TXT verification records, name servers) to find the e-mail provider and services the company verified its domain with (Google Workspace, Microsoft 365, HubSpot, Atlassian, Zoom, DocuSign, SendGrid ...). These are listed with evidence starting with "dns:" and source "dns". Free, adds about 1 second per site.

## `maxSites` (type: `integer`):

Cost cap: stop after this many sites.

## `timeoutSecs` (type: `integer`):

How long to wait for each page.

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

How many sites are analyzed at the same time.

## `httpVia` (type: `string`):

Pages are fetched from Apify's network first because response headers and cookies reveal the server, CDN and hosting. If a site blocks that request, Auto reads the page's script and stylesheet URLs through our servers (and a real browser if needed). If those routes are refused, Auto also tries our second server (Oracle, different IP) before a real browser.

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

Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account.

## Actor input object example

```json
{
  "urls": [
    "wordpress.org",
    "allbirds.com",
    "vercel.com",
    "python.org"
  ],
  "extraPages": [],
  "minConfidence": "medium",
  "includeEvidence": true,
  "includeDns": true,
  "maxSites": 1000,
  "timeoutSecs": 25,
  "maxConcurrency": 10,
  "httpVia": "auto"
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

## `full` (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": [
        "wordpress.org",
        "allbirds.com",
        "vercel.com",
        "python.org"
    ]
};

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

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

```

## MCP server setup

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