# Website Tech Stack Detector: CMS, Versions & CDN, No Login (`cliqtomedia/tech-stack-detector`) Actor

Find the live technology stack of up to 1,000 websites: CMS, ecommerce, analytics, CDN, server, frameworks and payments with versions and confidence. 7,600+ fingerprints, one row per site. No login.

- **URL**: https://apify.com/cliqtomedia/tech-stack-detector.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (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

from $1.50 / 1,000 site checkeds

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

Find out which technologies a website uses today. Give a list of domains or URLs, and the Actor loads each page, matches it against 7,600+ technology fingerprints and returns one row per site: CMS, ecommerce platform, analytics, CDN, web server, frameworks and payments, with versions and confidence where the page shows them. No login, no API key.

It is for sales and lead generation teams, market researchers, agencies and developers. The smallest run is the prefilled input `apify.com`, `shopify.com`, `wordpress.org`: three rows in a few seconds, for about $0.01 on the Apify Free plan.

![Website Tech Stack Detector: a list of website URLs is checked live (HTML, headers, scripts, cookies) and returns technologies with versions and categories, such as WordPress 7.2, Gutenberg 24.1.0 and Nginx for wordpress.org](https://api.apify.com/v2/key-value-stores/2twL64fCqTwsns9Kc/records/hero.png)

### Table of contents

- [What it does](#what-it-does)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing and limits](#pricing-and-limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Support](#support)

### What it does

For each URL, the Actor downloads the live page and looks at what the site sends: HTML and meta tags, response headers, script URLs, cookie names and page structure. It matches these signals against the open Wappalyzer-style fingerprint set (7,600+ technologies in 109 categories) and writes one Dataset row per site.

- **Technologies list.** Each technology has a name, categories, a version (when the page shows one) and a confidence from 0 to 100.
- **Ready columns.** `cms`, `ecommerce`, `analytics`, `cdn`, `server`, `framework` and `payment` are short lists of names, so you can filter a table without opening the full list.
- **Live result.** The check happens during your run. `detectedAt` says when, and `finalUrl` shows where redirects ended.
- **Optional browser mode.** With `useBrowser: true`, each page is opened in headless Chrome. This also finds technologies that appear only after JavaScript runs (JavaScript globals, background requests). It is slower and costs more.
- **Clear errors.** A site that cannot be checked gets a row with `status: "error"` and a code like `DNS_NOT_FOUND`, `BLOCKED` or `TIMEOUT`. An error is never shown as an empty stack.

#### Good fit

- Find leads that use a given platform: run your domain list and filter `ecommerce` for Shopify or `cms` for WordPress.
- Check a competitor's or a client's stack before a pitch or an audit: CMS version, analytics, tag manager, CDN.
- Enrich a CRM or spreadsheet with technographic columns for many domains in one run.
- Give an AI agent a quick way to answer "what is this site built with?".

#### Not a fit / limits

- One page per input URL. The Actor does not crawl the whole site. Technologies used only on other pages (checkout, blog, app) are found only if you add those URLs.
- No history. It shows what the page uses now, not what the site used in the past. It does not check DNS, mail (MX/SPF) or SSL records.
- No security or vulnerability scanning, and no logged-in pages. Only public pages are checked; local, private and internal addresses are refused.
- Detection is based on public signals. A technology that leaves no trace in the page, headers or scripts cannot be found. Sites behind strong bot protection may return `BLOCKED`.

### Quick start

1. Open the **Input** tab. The **Websites** field is prefilled with `apify.com`, `shopify.com` and `wordpress.org`.
2. Replace them with your own domains or URLs, one per line (up to 1,000; up to 500 in browser mode).
3. Keep **Render JavaScript in a browser** off (the default) for a fast, cheap check.
4. Click **Start**.
5. Open the **Output** tab. The **Overview** view shows one row per site with `cms`, `ecommerce`, `analytics`, `cdn` and `server`. The **Technologies** view shows the full list.

Expected result: for the prefill, three `complete` rows. wordpress.org shows WordPress with its version and Nginx; shopify.com shows Shopify and Cloudflare; apify.com shows Amazon CloudFront and React. A run like this usually takes under 20 seconds.

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next integration |
| --- | --- | --- | --- | --- |
| Build a lead list by platform | `urls` (domains from your CRM) | One row per site | `url`, `ecommerce`, `cms`, `technologyCount` | Export CSV, filter, import to CRM |
| Competitor or client audit | `urls` + `useBrowser: true` | Full technology list | `technologies[].name`, `version`, `categories` | Report or spreadsheet |
| Check one category only | `urls` + `categories: ["Analytics"]` | Filtered technology list | `technologies`, `analytics` | Google Sheets |
| Regular stack check | `urls` on a schedule | Row with date | `detectedAt`, `cms`, `cdn` | Compare runs over time |
| AI agent question | `urls` (MCP) | One row per site | `cms`, `ecommerce`, `analytics`, `cdn`, `server` | Agent answer |

### Input

#### Input fields

| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example | Recommendation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `urls` | Websites | array of strings | yes | prefill `["apify.com", "shopify.com", "wordpress.org"]` | 1–1,000 domains or `http(s)` URLs (500 in browser mode) | Each line is one page to check | `["shopify.com", "https://example.com/blog"]` | Use the page that matters to you; a bare domain means its home page |
| `useBrowser` | Render JavaScript in a browser | boolean | no | default `false` | `true`, `false` | Opens each page in headless Chrome to also find JavaScript-only technologies | `true` | Keep off for big lists; turn on for a detailed audit |
| `categories` | Only these categories | array of strings | no | default `[]` (all) | names from the list in the Input tab, e.g. `CMS`, `Ecommerce`, `Analytics`, `CDN` | Keeps only these categories in `technologies` | `["CMS", "Ecommerce"]` | Use when you need one kind of technology. The summary columns are always filled |
| `maxConcurrency` | Parallel sites | integer | no | default `10` | 1–20 (browser mode: at most 5) | How many sites are checked at the same time | `20` | The default is fine for most runs |

How URLs are read:

- A bare domain such as `shopify.com` is checked at `https://shopify.com/`. A full URL is checked as given (the `#fragment` is ignored).
- Duplicates are checked once, even with different case, a trailing `/` or an IDN vs punycode spelling. The count is in the status message and in `RUN_SUMMARY.duplicatesSkipped`.
- Lines that cannot be a public website are not checked and do not get a Dataset row: text that is not a URL, schemes other than `http`/`https`, ports other than 80 and 443, URLs with a user name or password, `localhost`, and private or local IP addresses. They are counted in `RUN_SUMMARY.invalidSkipped`, and up to 50 examples are listed in `RUN_SUMMARY.invalidSamples` (passwords are replaced with `***`). The other lines are still checked.
- If no line is valid, or the list is empty, the run stops at once with a clear input error.

#### Interacting settings

| When | Input A | Input B | Effective behavior | Example | Cost/result effect |
| --- | --- | --- | --- | --- | --- |
| Browser mode | `useBrowser: true` | `maxConcurrency` | At most 5 pages at the same time | `maxConcurrency: 20` → 5 | Browser price per site; more memory |
| Category filter | `categories` | summary columns | `technologies` is filtered; `cms`, `ecommerce`, `analytics`, `cdn`, `server`, `framework`, `payment` are always filled | `["Web servers"]` | No price change |
| Big lists | `urls` > 20 (HTTP) or > 10 (browser) | default memory | The run gets more memory (1 GB for HTTP, 2 GB for browser) | 500 domains | See [Pricing](#pricing-and-limits) for the start charge per GB |

#### Input examples

##### Quick check of a domain list

```json
{
  "urls": ["apify.com", "shopify.com", "wordpress.org"]
}
```

Expected behavior: one HTTP request per site, 10 sites at a time.
Expected output: three rows with `cms`, `ecommerce`, `analytics`, `cdn`, `server` and the full `technologies` list.
Limit or cost note: one `site-checked` event per site plus the start charge.

##### Detailed audit with JavaScript

```json
{
  "urls": ["https://www.allbirds.com/", "https://techcrunch.com/"],
  "useBrowser": true
}
```

Expected behavior: each page is opened in headless Chrome.
Expected output: usually more technologies per site than HTTP mode, for example JavaScript frameworks and tag libraries.
Limit or cost note: one `site-checked-browser` event per site; about 3–4 seconds per site.

##### Only CMS and ecommerce

```json
{
  "urls": ["example-shop.com", "example-blog.com"],
  "categories": ["CMS", "Ecommerce"]
}
```

Expected behavior: the same check as usual.
Expected output: `technologies` has only CMS and Ecommerce entries.

### Output

Results go to the default **Dataset**, one row per checked site. There are two views: **Overview** (summary columns) and **Technologies** (full list). The **Key-value store** has a `RUN_SUMMARY` record with counts, error codes, skipped lines and limits.

#### Output example

The wordpress.org row from the prefill run (the technologies list is shortened):

```json
{
  "url": "wordpress.org",
  "finalUrl": "https://wordpress.org/",
  "status": "complete",
  "httpStatus": 200,
  "technologies": [
    { "name": "WordPress", "categories": ["CMS", "Blogs"], "version": "7.2", "confidence": 100, "website": "https://wordpress.org" },
    { "name": "Gutenberg", "categories": ["WordPress plugins", "Editors"], "version": "24.1.0", "confidence": 100, "website": "https://github.com/WordPress/gutenberg" },
    { "name": "Nginx", "categories": ["Web servers", "Reverse proxies"], "version": null, "confidence": 100, "website": "https://nginx.org/en" },
    { "name": "Google Tag Manager", "categories": ["Tag managers"], "version": null, "confidence": 100, "website": "https://www.google.com/tagmanager" }
  ],
  "technologyCount": 13,
  "cms": ["WordPress"],
  "ecommerce": [],
  "analytics": [],
  "cdn": [],
  "server": ["Nginx"],
  "framework": [],
  "payment": [],
  "mode": "http",
  "pageTitle": "Blog Tool, Publishing Platform, and CMS – WordPress.org",
  "detectedAt": "2026-10-14T02:41:57.061Z",
  "fingerprintsVersion": "enthec/webappanalyzer@eea872a",
  "fingerprintsLicence": "GPL-3.0-only",
  "error": null
}
```

A row for a domain that does not exist:

```json
{
  "url": "no-such-domain-example.com",
  "finalUrl": null,
  "status": "error",
  "httpStatus": null,
  "technologies": [],
  "technologyCount": 0,
  "cms": [],
  "ecommerce": [],
  "analytics": [],
  "cdn": [],
  "server": [],
  "framework": [],
  "payment": [],
  "mode": "http",
  "pageTitle": null,
  "detectedAt": "2026-10-14T02:15:41.864Z",
  "fingerprintsVersion": "enthec/webappanalyzer@eea872a",
  "fingerprintsLicence": "GPL-3.0-only",
  "error": { "code": "DNS_NOT_FOUND", "message": "Domain does not resolve" }
}
```

An empty list (`[]`) on a `complete` row means the page was checked and nothing of that kind was found. On an `error` row the lists are empty because the page could not be checked; read `error` instead.

#### Output fields

| Field | Meaning | Type | Can be empty? | Example / enum | Source / notes |
| --- | --- | --- | --- | --- | --- |
| `url` | Your input line | string | no | `wordpress.org` | As given |
| `finalUrl` | Page URL after redirects | string or null | null if no page was received | `https://wordpress.org/` | |
| `status` | Row result | string | no | `complete`, `error` | |
| `httpStatus` | HTTP status of the final page | integer or null | yes | `200`, `404` | |
| `technologies` | Technologies found | array of objects | `[]` if none | `{name, categories, version, confidence, website}` | Filtered by `categories` if set |
| `technologies[].version` | Version, if the page shows it | string or null | often null | `7.2` | From meta tags, script URLs, headers |
| `technologies[].confidence` | How sure the match is | integer | no | `100` | 0–100, from the fingerprint set |
| `technologyCount` | Number of technologies in the list | integer | no | `13` | |
| `cms` | CMS names | array of strings | `[]` if none | `["WordPress"]` | Always from all categories |
| `ecommerce` | Ecommerce platforms | array of strings | `[]` if none | `["Shopify"]` | |
| `analytics` | Analytics tools | array of strings | `[]` if none | `["Leadfeeder"]` | |
| `cdn` | CDNs | array of strings | `[]` if none | `["Cloudflare"]` | |
| `server` | Web servers and reverse proxies | array of strings | `[]` if none | `["Nginx"]` | Frameworks are in `framework` |
| `framework` | Web and JavaScript frameworks | array of strings | `[]` if none | `["Next.js"]` | |
| `payment` | Payment processors | array of strings | `[]` if none | `["Stripe"]` | |
| `mode` | How the page was loaded | string | no | `http`, `browser` | |
| `pageTitle` | Page `<title>` | string or null | yes | | |
| `detectedAt` | When the page was checked | string | no | ISO 8601 UTC | |
| `fingerprintsVersion` | Fingerprint data used | string | no | `enthec/webappanalyzer@eea872a` | |
| `fingerprintsLicence` | Licence of the fingerprint data | string | no | `GPL-3.0-only` | |
| `error` | Why the site could not be checked | object or null | null on `complete` rows | `{ "code": "TIMEOUT", "message": "…" }` | Codes below |

Error codes: `DNS_NOT_FOUND` (domain does not resolve), `TIMEOUT`, `BLOCKED` (the site answered 401, 403 or 429), `HTTP_<status>` (for example `HTTP_404`, `HTTP_500`), `NOT_HTML` (the URL is a file or JSON, not a web page), `TLS_ERROR`, `TOO_MANY_REDIRECTS`, `NETWORK_ERROR`, `PRIVATE_ADDRESS` and `INVALID_URL` (a redirect or DNS answer led to a private or invalid address), `INTERNAL_ERROR` (our side failed).

#### Output entities and surfaces

| Situation | Dataset/API/KVS | Result shape | User action |
| --- | --- | --- | --- |
| Normal item | Dataset | `complete` row with technologies and summary columns | Filter, sort, export |
| Page checked, nothing found | Dataset | `complete` row, `technologies: []` | The page has no known signals; try another page of the site or browser mode |
| Site could not be checked | Dataset | `error` row with `error.code` | See [Troubleshooting](#troubleshooting) |
| Bad input lines | `RUN_SUMMARY` | `invalidSkipped`, `invalidSamples` | Fix those lines and run them again |
| Run stopped early | `RUN_SUMMARY` + status message | `status: "partial"`, `reason: "CHARGE_LIMIT"` or `"TIME_LIMIT"` | Raise the limit or timeout, run the rest |

#### Moving from BuiltWith-style output

If you used an Actor with `domain` and `techs[{name, tag, categories, link}]`, use this map: `domain` → `url` (or `finalUrl`), `techs` → `technologies`, `techs[].link` → `technologies[].website`. New here: `version`, `confidence`, `detectedAt`, `status`, `error` and the ready columns. The results differ by design: this Actor shows what the page uses now, not a history of the domain.

### Pricing and limits

#### Pricing unit

This Actor uses pay-per-event pricing. Platform usage is included.

| Event | When | Free plan | Bronze | Silver | Gold and higher |
| --- | --- | ---: | ---: | ---: | ---: |
| Actor start | Once per run for each GB of memory (512 MB and 1 GB runs: once; 2 GB: twice) | $0.007 | $0.007 | $0.007 | $0.007 |
| Site checked (`site-checked`) | Each site the Actor contacted in HTTP mode | $0.0018 | $0.0017 | $0.0016 | $0.0015 |
| Site checked in a browser (`site-checked-browser`) | Each site the Actor contacted with `useBrowser: true` | $0.0035 | $0.0033 | $0.0031 | $0.003 |

A site is charged when the Actor actually contacted it: a `complete` row, and also an error that the site or network gave (`BLOCKED`, `TIMEOUT`, `DNS_NOT_FOUND`, `HTTP_404` and so on), because the check was done. Free: duplicates, input lines rejected before any request (listed in `RUN_SUMMARY`), `INTERNAL_ERROR` rows and sites not started because the run was cancelled. A run with invalid input pays only the start charge.

You can set a maximum total charge for a run. The Actor stops at that limit, keeps the rows it paid for and marks `RUN_SUMMARY` with `status: "partial"` and `reason: "CHARGE_LIMIT"`.

#### Cost examples

Prices on the Free plan, with the default memory.

| Scenario | Input scope | Charged sites | Start | Site charges | Total |
| --- | --- | ---: | ---: | ---: | ---: |
| Prefill | 3 domains, HTTP | 3 | $0.007 | $0.0054 | $0.0124 |
| 100 domains | HTTP, 1 GB | 100 | $0.007 | $0.18 | $0.187 |
| 1,000 domains | HTTP, 1 GB | 1,000 | $0.007 | $1.80 | $1.807 |
| Browser audit | 5 sites, browser, 1 GB | 5 | $0.007 | $0.0175 | $0.0245 |
| Browser list | 100 sites, browser, 2 GB | 100 | $0.014 | $0.35 | $0.364 |
| Invalid input | no valid line (2 GB browser runs: two starts, $0.014) | 0 | $0.007 | $0 | $0.007 |

On Gold and higher plans, 1,000 domains in HTTP mode cost $0.007 + $1.50 = $1.507.

#### Limits

- Up to 1,000 URLs per run in HTTP mode and up to 500 in browser mode. One page per URL.
- Each request has 15 seconds, and each site 30 seconds in total, with up to 3 attempts after network errors, timeouts, 429 and 5xx answers. Up to 5 redirects. Only the first 5 MB of a page are read.
- Speed: in HTTP mode, 1,000 domains took about 11 minutes in our test; in browser mode, about 3–4 seconds per site (100 sites in about 6 minutes), so 500 sites fit the default one-hour timeout. In browser mode each site has at most 45 seconds.
- If the run is near its timeout, the Actor stops starting new sites and saves what it has (`reason: "TIME_LIMIT"`).
- The fingerprint data is [enthec/webappanalyzer](https://github.com/enthec/webappanalyzer) (GPL-3.0), used unchanged. Technology names and trademarks belong to their owners.

### API, export and integrations

- **API:** start a run with `POST https://api.apify.com/v2/acts/cliqtomedia~tech-stack-detector/runs` and the input JSON, then read the default Dataset. The **API** tab has ready code. See the [Apify API docs](https://docs.apify.com/api/v2).
- **Export:** download the Dataset as CSV, Excel, JSON or XML from the Output tab. The **Overview** view gives flat columns for spreadsheets.
- **Scheduling:** create an [Apify schedule](https://docs.apify.com/platform/schedules) to check the same list every week or month. Each row has `detectedAt`, so you can compare stacks over time.
- **No-code:** connect the Actor in Make, Zapier or n8n with the Apify integration.

#### Use with AI agents (MCP)

- MCP server with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/tech-stack-detector` (see [Apify MCP server](https://docs.apify.com/platform/integrations/mcp)).
- Smallest useful input: `{"urls": ["apify.com", "shopify.com", "wordpress.org"]}`
- Key output fields: `cms`, `ecommerce`, `analytics`, `cdn`, `server` (ready lists of names), `technologies[].name` and `version`, `status` (`complete` / `error`) and `error.code`.
- A run with a few domains usually finishes in under 20 seconds and costs $0.007 plus $0.0018 per site on the Free plan. Up to 1,000 URLs per run (500 in browser mode), one page per URL, public pages only.

### Troubleshooting

#### Many BLOCKED rows

Symptom: rows with `error.code: "BLOCKED"`.
Likely reason: the site answered 401, 403 or 429. Some sites block automated requests from cloud servers.
Check: open the URL in your browser; a login wall or a bot check page confirms it.
Action: try `useBrowser: true` for those sites. Some sites block both modes; the Actor does not try to bypass bot protection.

#### A complete row has few or no technologies

Symptom: `status: "complete"`, but `technologies` is short or empty.
Likely reason: the page loads its tools with JavaScript, or it is a simple page.
Action: run it again with `useBrowser: true`, or add a more typical page of the site (for example a product page).

#### TIMEOUT or NETWORK_ERROR

Symptom: rows with `TIMEOUT` or `NETWORK_ERROR`.
Likely reason: the site is slow, down or does not answer cloud servers.
Action: run those URLs again later. Each attempt is limited to 15 seconds.

#### Some of my lines are missing from the Dataset

Symptom: fewer rows than input lines.
Likely reason: duplicates, or lines that are not a public website URL.
Check: `RUN_SUMMARY.duplicatesSkipped`, `invalidSkipped` and `invalidSamples`.
Action: fix the lines listed in `invalidSamples` and run them again.

### FAQ

#### Do I need an account, cookies or an API key for the sites?

No. The Actor reads public pages like a visitor who is not logged in.

#### Why does it find fewer technologies than BuiltWith?

BuiltWith shows everything it has seen on a domain over the years, plus DNS and mail records. This Actor checks the live page now. You get fewer, but current entries, with versions and confidence.

#### Am I charged for sites that fail?

A site that the Actor contacted is charged even if it answers with an error, because the check was done and the error is the result. Duplicates, rejected input lines, our own internal errors and cancelled sites are free.

#### How accurate is the version?

The version comes from what the page shows: a meta generator tag, a script file name or a header. Many sites hide versions, so `version` is often `null`.

### Support

For bugs, questions or feature requests, open an issue in the **Issues** tab. Include the run ID, the mode (HTTP or browser) and the URLs that look wrong. Do not send tokens or private data.

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/tech-stack-detector/changelog.md

# Actor input Schema

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

Up to 1,000 domains or URLs (500 in browser mode), one per line, such as shopify.com or https://example.com/blog. A bare domain is checked at https://domain/. Each URL is one page (no crawling). Duplicates are checked once. Public pages only: private, local and internal addresses are refused.

## `useBrowser` (type: `boolean`):

Off (default): one fast HTTP request per site, which finds most CMS, ecommerce, analytics, CDN and server technologies. On: load each page in headless Chrome to also detect technologies visible only after JavaScript runs (JS globals, XHR hosts). Slower (about 3–4 s per site), up to 500 URLs per run, uses more memory and is billed as a browser analysis.

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

Optional filter for the technologies list, for example CMS, Ecommerce, Analytics, CDN. Empty means all 109 categories. The cms, ecommerce, analytics, cdn, server, framework and payment columns are always filled.

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

How many sites are analysed at the same time (1–20; browser mode uses at most 5).

## Actor input object example

```json
{
  "urls": [
    "apify.com",
    "shopify.com",
    "wordpress.org"
  ],
  "useBrowser": false,
  "categories": [],
  "maxConcurrency": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `technologies` (type: `string`):

No description

## `summary` (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": [
        "apify.com",
        "shopify.com",
        "wordpress.org"
    ]
};

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

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

```

## MCP server setup

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