Website Tech Stack Detector: frameworks, CMS, analytics
Pricing
from $0.005 / actor start
Website Tech Stack Detector: frameworks, CMS, analytics
Detect the technology stack behind any list of websites: CMS, ecommerce platform, JavaScript framework, analytics, CDN, web server and more, with versions, confidence and the evidence for every match.
Pricing
from $0.005 / actor start
Rating
0.0
(0)
Developer
Michal Pšenčík
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Website Tech Stack Detector (frameworks, CMS, analytics)
What does Website Tech Stack Detector do?
Give this Actor a list of websites and it tells you what each one is built with: the CMS, the ecommerce platform, the JavaScript framework, the analytics and tag managers, the CDN, the web server, the programming language and around a hundred other categories. Every match comes back with its category, its version where the site gives one away, a confidence score and the evidence — the exact header, cookie, script URL, meta tag or HTML pattern that produced it, so you can check a result instead of trusting it.
It fetches each site over plain HTTP, downloads its script files and searches those too. There is no browser, so a thousand domains cost you a few minutes rather than an hour.
Try it from the Input tab: leave everything at its default and hit Start to profile three well-known sites.
Why use Website Tech Stack Detector?
- Sales prospecting. Filter a list of domains down to the ones running Shopify, HubSpot, WordPress or a competitor's product, then hand your team the ones worth calling.
- Competitive and market research. Take any list of companies and count what they actually run, rather than what a vendor's case-study page claims.
- Lead enrichment inside your own funnel. Feed the domains from your signup form through the Actor and attach the stack to each record in your CRM.
- Security and compliance sweeps. Spot the sites in your estate still advertising an old server, an outdated jQuery, or a tag manager nobody remembers adding.
- Agency audits. Produce the technology inventory slide for a pitch from a URL list in one run.
Every technology carries the evidence that produced it, which is the part most detectors leave out. When a result looks wrong you can see whether it came from a real fingerprint or from a Content-Security-Policy header that merely mentions a vendor.
How to use Website Tech Stack Detector
- Click Try for free (or open the Actor in Apify Console).
- On the Input tab, put your websites in Websites to analyse. You can paste them one per line,
upload a CSV or text file, or link a Google Sheet. A bare domain like
example.comworks; it is read ashttps://example.com. - Leave the rest alone for a first run. If you want fewer weak matches, raise Minimum confidence. If a site hides its stack deep in its JavaScript bundles, raise Scripts to download per site.
- Click Start. Ten sites take about ten seconds.
- Open the Output tab (or Storage → Dataset) and export as JSON, CSV, Excel or HTML, or pull it through the Apify API.
For a recurring inventory, use Schedules in Apify Console to re-run the same list every week and diff the results.
Input
All fields are optional; the defaults profile three sample sites.
| Field | Type | Default | Description |
|---|---|---|---|
startUrls | array | three sample sites | The websites to analyse. One dataset item comes back per URL. Accepts { "url": "..." } objects, a pasted list, an uploaded file or a Google Sheet. |
maxItems | integer | 1000 | Stop after this many URLs. 0 means no limit. |
maxScripts | integer | 8 | How many external JavaScript files to download and search per site. Inline scripts are always searched. 0 skips external scripts and makes runs faster. |
minConfidence | integer | 0 | Drop technologies detected below this confidence, 0 to 100. |
includeFailedUrls | boolean | true | Write a row for a URL that could not be fetched, with the error in it. A failed row is a dataset item and is billed as one. Set to false to skip them. |
proxyConfiguration | object | Apify proxy disabled | Optional. The Actor fetches the URLs you supply, so no proxy is needed for most lists. |
Example input:
{"startUrls": [{ "url": "https://wordpress.org" }, { "url": "https://www.shopify.com" }],"maxScripts": 8,"minConfidence": 0}
Output
Each website is one dataset item. Download the dataset in various formats such as JSON, HTML, CSV, or Excel.
The example below is a real run against https://wordpress.org, trimmed to five of the thirteen
technologies it found and to the first two pieces of evidence each.
{"url": "https://wordpress.org/","finalUrl": "https://wordpress.org/","statusCode": 200,"success": true,"error": null,"technologies": [{"name": "WordPress","categories": ["CMS", "Blogs"],"website": "https://wordpress.org","version": "7.2","confidence": 100,"evidence": [{"type": "header","key": "link","pattern": "rel=\"https://api\\.w\\.org/\"","value": "rel=\"https://api.w.org/\""},{"type": "meta","key": "generator","pattern": "^WordPress(?: ([\\d.]+))?","value": "WordPress 7.2"}]},{"name": "Gutenberg","categories": ["WordPress plugins", "Editors"],"website": "https://github.com/WordPress/gutenberg","version": "23.9.0","confidence": 100,"evidence": [{"type": "scriptSrc","pattern": "/wp-content/plugins/gutenberg/","value": "/wp-content/plugins/gutenberg/","source": "https://wordpress.org/wp-content/plugins/gutenberg/build/modules/block-library/navigation/view.min.js?ver=1bf28ded04f9f188bdcb-20240308"},{"type": "dom","key": "link[href*='/wp-content/plugins/gutenberg/'][href]","pattern": "/wp-content/plugins/gutenberg/.+\\.css(?:\\?ver=(\\d+(?:\\.\\d+)+))?","value": "/wp-content/plugins/gutenberg/build/styles/block-library/navigation/style.min.css?ver=23.9.0"}]},{"name": "Nginx","categories": ["Web servers", "Reverse proxies"],"website": "https://nginx.org/en","version": null,"confidence": 100,"evidence": [{"type": "header","key": "server","pattern": "nginx(?:/([\\d.]+))?","value": "nginx"}]},{"name": "Google Tag Manager","categories": ["Tag managers"],"website": "https://www.google.com/tagmanager","version": null,"confidence": 100,"evidence": [{"type": "html","pattern": "googletagmanager\\.com/ns\\.html[^>]+></iframe>","value": "googletagmanager.com/ns.html?id=GTM-P24PF4B\" height=\"0\" width=\"0\" style=\"display:none;visibility:hidden\"></iframe>"}]},{"name": "MySQL","categories": ["Databases"],"website": "https://mysql.com","version": null,"confidence": 100,"evidence": [{"type": "implied","pattern": "WordPress","value": "implied by WordPress"}]}],"technologyNames": ["Google Font API","Google Tag Manager","Gutenberg","HSTS","MySQL","Nginx","Open Graph","PHP","Priority Hints","RSS","WordPress","WordPress Block Editor","WordPress Site Editor"],"technologyCount": 13,"categories": ["Blogs","CMS","Databases","Editors","Font scripts","Miscellaneous","Page builders","Performance","Programming languages","Reverse proxies","Security","Tag managers","Web servers","WordPress plugins"],"scriptsFetched": 5,"detectedAt": "2026-09-03T17:01:44.906Z"}
A URL that could not be fetched comes back as a row too, so nothing disappears from your list silently:
{"url": "https://this-domain-does-not-exist-98765.com/","finalUrl": null,"statusCode": null,"success": false,"error": "getaddrinfo ENOTFOUND this-domain-does-not-exist-98765.com","technologies": [],"technologyNames": [],"technologyCount": 0,"categories": [],"scriptsFetched": 0,"detectedAt": "2026-09-03T17:02:18.676Z"}
Data fields
| Field | Description |
|---|---|
url | The URL as you supplied it. |
finalUrl | Where it ended up after redirects. |
statusCode | HTTP status of the final response. A 4xx or 5xx page is still analysed, because it still has headers. |
success | Whether the site could be fetched at all. |
error | Why it could not, when it could not. |
technologies | Every detected technology, most confident first. |
technologyNames | The same names as a flat list, so CSV and Excel exports stay readable. |
categories | Every category present on the site, deduplicated. |
scriptsFetched | How many external script files were downloaded and searched. |
detectedAt | When the run started, in UTC. |
Inside technologies, evidence[].type tells you where a match came from: header, cookie, meta,
scriptSrc, script (inside a downloaded JavaScript file), html, text, css, url, dom, or
implied when one technology necessarily brings another (WordPress implies PHP and MySQL).
How much does it cost to detect a website's tech stack?
This Actor uses pay per event pricing: $0.002 per website analysed, plus $0.005 per run start. Platform usage is included, so those two numbers are the whole bill.
| Websites in the run | Cost |
|---|---|
| 10 | $0.03 |
| 100 | $0.21 |
| 1,000 | $2.01 |
| 10,000 | $20.01 |
The most-used competing Actor on this Store charges $0.10 per detection, so the same 10,000 domains cost $1,000 there.
There are Actors on this Store charging a tenth of this, and they return a list of names: no version, no confidence, and no evidence you can check. This one also makes up to nine HTTP requests per site (the page plus its scripts) rather than one, which is what finds the bundler, the JavaScript library and the analytics tag that a single fetch of the HTML misses.
The Apify free plan comes with $5 of monthly platform credit, which covers around 2,000 websites.
To cap spending on a single run, set a maximum cost in the run options, or use the maxItems input.
Tips and advanced options
- Analyse a deep page, not just the home page. A checkout URL reveals the payment processor, a blog post reveals the CMS plugins. The Actor fetches exactly the URL you give it and does not crawl.
- Turn off external scripts for a fast, cheap pass.
maxScripts: 0cuts each site to one request. You keep every header, cookie, meta, HTML and inline-script fingerprint and lose only the ones that live inside bundled JavaScript files. Raise it to 30 when you care more about depth than speed. - Pay only for what was analysed. Set
includeFailedUrls: falseand dead domains cost nothing. - Cut the noise.
minConfidence: 100keeps only technologies backed by a strong or a repeated signal. - Exporting to a spreadsheet? Use
technologyNamesandcategories. The fulltechnologiesarray is nested and flattens badly into CSV. - Deduplicate on
finalUrl. Two input URLs that redirect to the same site produce two rows. - Proxies are optional. You are fetching your own list of URLs, so there is no anti-bot surface. Turn on Apify Proxy datacenter groups only if a site refuses the platform's own IP range.
What it can and cannot see
The Actor reads what a site sends over HTTP: response headers, cookies, the HTML, the meta tags, the script URLs, and the contents of the JavaScript files it downloads. Roughly 86% of the fingerprint set can be evaluated that way.
The rest cannot, and the Actor does not pretend otherwise. Fingerprints that need a running JavaScript context (a global variable a framework sets at runtime), a DNS lookup, or a TLS certificate issuer are not evaluated. In practice this mostly costs you single-page applications that leave no trace in their server-rendered HTML.
FAQ, disclaimers and support
Does this Actor need an account or an API key anywhere? No. It fetches public pages with no login of any kind, and it uses no third-party detection API.
Is it legal to detect a website's technology stack? The Actor requests public pages the way a browser does and reads only what the server chooses to send. It does not log in, does not bypass any access control and does not collect personal data. You are responsible for the list of URLs you supply and for how you use the results.
Why did it miss React (or Vue, or Angular) on a site I know uses it? Those frameworks are often detected by a variable in the running page, which needs a browser. This Actor is HTTP-only by design, because that is what makes it fast and cheap. When a framework leaves a trace in the markup or the script URLs it is found; a fully client-rendered app with a generic bundle name may not be.
Why is a technology listed that the site does not obviously use? Check the evidence. Some fingerprints match a vendor named in a Content-Security-Policy header, which proves the site allows the vendor rather than that it currently loads it. Those rows say
"type": "header", "key": "content-security-policy"What does confidence mean? It is the fingerprint author's weighting, not a probability. A single weak pattern scores 40 or 50, several independent patterns add up, and the total caps at 100.
Why is version usually null?
Only some fingerprints capture a version, and only when the site exposes one, typically in a ?ver= query string,
a meta generator tag or a server header. Most sites strip those.
Does it crawl the site? No. One request per URL you supply, plus its script files. If you want several pages of one site, put several URLs in the input.
Where do the fingerprints come from and how fresh are they? From a snapshot of the community Wappalyzer fingerprint set maintained at enthec/webappanalyzer, which is licensed GPL-3.0. Wappalyzer's own repository is gone and its npm package is unmaintained, so the community forks are where the data lives now. Around 7,600 technologies across 109 categories are covered. The snapshot is refreshed when the Actor is rebuilt.
Found a bug, or need something else? Open an issue on the Issues tab of this Actor and it will be picked up. If you need a fingerprint that is missing, or a browser-based version that can read JavaScript globals, mention it in the issue.