Website Tech Stack avatar

Website Tech Stack

Pricing

$3.00 / 1,000 analysed urls

Go to Apify Store
Website Tech Stack

Website Tech Stack

Detect the CMS, frameworks, analytics, CDN and server behind any URL list from HTML and response headers alone — no browser, no per-domain markup.

Pricing

$3.00 / 1,000 analysed urls

Rating

0.0

(0)

Developer

openrows

openrows

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Detect the CMS, JavaScript/web frameworks, analytics, CDN and web server behind any list of URLs — a fast, honest technology-stack detector for growth, sales-ops, SEO and competitive research.

Give it a list of URLs and it delivers a clean dataset (JSON, CSV, Excel, or through the API) of the technologies each page is built on. HTTP-only, no browser: one plain request per URL, so runs are fast and cheap.

Site-agnostic. You supply the URLs; this Actor only fetches what you point it at. See "Data and compliance" below.

What data you get

For every URL the Actor returns:

FieldDescription
urlThe URL that was analysed
scrapedAtISO 8601 timestamp the result was written to the dataset
fetchedAtISO 8601 timestamp the page was fetched
serverDetected web server / reverse proxy, or the raw Server header if nothing matched
cmsDetected CMS technologies (array)
frameworksDetected JS / web / UI frameworks (array)
analyticsDetected analytics and tag-manager technologies (array)
cdnDetected CDN technologies (array)
technologiesEvery detected technology; shape depends on input (see below)
httpHeadersA small, fixed subset of the page's own response headers (see below)

technologies[] is, by default, { name, category, version, confidence } per technology. With compactOutput: true it drops to { name, category }. With categoriesOnly: true it drops further, to a flat list of the distinct category names detected (no technology names, no versions) — the smallest, cheapest shape.

httpHeaders is exactly these response header names, when present on the page: server, x-powered-by, via, x-generator, x-drupal-cache, x-varnish, x-cache, cf-ray, cf-cache-status, x-shopify-stage, x-aspnet-version, x-aspnetmvc-version, x-turbo-charged-by, x-sourcemap — never the full header set, never Set-Cookie.

Input

OptionTypeDefaultDescription
urlsarray of strings—Pages to analyse, one per line. One result per URL. Required.
maxItemsinteger100Stop after this many results (max 10,000). Also your cost cap.
categoriesOnlybooleanfalseReturn only the distinct technology categories per URL, nothing more.
compactOutputbooleanfalseDrop httpHeaders and per-technology version/confidence detail.
proxyConfigurationobjectApify datacenterProxy settings

urls takes plain URL strings, one per line in the Console editor (or a plain array of strings through the API) — not the "Load URLs from a file" bulk-import mode some other Actors offer.

Example input:

{
"urls": ["https://docs.apify.com/", "https://example.com/"],
"maxItems": 50
}

Output example

This is a real result from this Actor's own run against https://wordpress.org/ (not invented): no Cloudflare and no jQuery front that page, and it carries Google Tag Manager, not Google Analytics.

{
"url": "https://wordpress.org/",
"scrapedAt": "2026-09-22T20:40:51.597Z",
"fetchedAt": "2026-09-22T20:40:51.597Z",
"server": "Nginx",
"cms": ["WordPress"],
"frameworks": [],
"analytics": [],
"cdn": [],
"technologies": [
{ "name": "WordPress", "category": "CMS", "version": "7.2", "confidence": 100 },
{ "name": "Google Tag Manager", "category": "Tag managers", "version": null, "confidence": 100 },
{ "name": "Nginx", "category": "Web servers", "version": null, "confidence": 100 }
],
"httpHeaders": { "server": "nginx" }
}

(Google Tag Manager's own category is "Tag managers", not "Analytics" — it does not appear in the analytics summary field, only in technologies[].)

A run summary (items pushed, items skipped by category — including robotsDisallowed and robotsUnknown — pages fetched, failed requests by category, stop reason, and a robots block with the per-host outcomes — see "How robots.txt is handled" below) is stored in the run's key-value store under the key OUTPUT.

Pricing

Pay per result. You are charged per result and nothing else: no platform usage, no compute units. Retries and failed requests are free. The current rate is on the Pricing tab of this Actor — that is the only place it is set, so no figure is repeated here.

  • maxItems caps the number of results, and therefore the cost, of a run.
  • The run also stops when it reaches the maximum total charge you set for the run in Apify Console or through the API.

Tips

  • Start with maxItems: 20 to check the fields before a large run.
  • Use categoriesOnly: true when you only need to know whether a site runs a CMS, uses a CDN, etc. — not which one or which version. It is the cheapest way to read the output, though the price per result is the same; it only shrinks the payload.
  • A handful of well-known frameworks (Angular 2+, Svelte, Ember.js, Adobe Analytics, and a few others — see Limitations) are not detectable from HTML and headers alone and are not in this Actor's fingerprint set.

Limitations

  • Detection is HTTP-only: one page load, no headless browser. Technologies whose only reliable signal is a DOM property, a JavaScript global, or a live probe request are not detected. The vendored fingerprint set (72 technologies — see below) was filtered down to exactly the technologies detectable this way; a small number of well-known frameworks that are only detectable via the DOM/JS (Angular 2+, Svelte, Ember.js, Material UI, Adobe Analytics, Cloudflare Turnstile, Remix, Bulma) are out of scope for this reason, not an oversight.
  • Linked CSS/JS assets are matched by filename/path only (as referenced in the page's own HTML) — their contents are never fetched. A technology whose only signature lives inside a script's body (not its filename, the page's HTML, headers, meta tags or cookie names) will not be detected.
  • Single-page apps that render most of their markup client-side may show fewer signals than a server-rendered page, because only the HTML delivered on the initial GET is inspected.
  • Pages behind a login are not accessible and are not scraped.

Source caveats. The fingerprint database is a curated subset (72 of several thousand upstream entries) of a community-maintained, open technology-detection database — coverage is necessarily narrower than a full commercial tool's, and a technology's absence from the output does not always mean the site doesn't use it (see Limitations above). See "Third-party data" below for the source and its license.

Data and compliance

This Actor is site-agnostic: you choose the URLs, and you are responsible for the sites you point it at. Within that:

  • It fetches only the URLs you give it — nothing is discovered, crawled or followed beyond them.
  • It identifies itself honestly with an openrows/website-tech-stack User-Agent and sends no headers that imitate a browser (no sec-ch-*, no sec-fetch-*, no Chrome fingerprint).
  • It does not bypass access controls, solve CAPTCHAs, or retry past a block (401/403/429 is recorded and the request is abandoned, not retried under a different identity).
  • It collects no personal data. The output is technology names, versions, categories and a small fixed subset of response headers (server/cache/CDN signals only). Cookie names are used as a detection signal (e.g. _ga implies Google Analytics); cookie values are never read or stored, and no Set-Cookie value ever reaches the output.

How robots.txt is handled

Before the first page on a host is fetched, the Actor fetches that host's robots.txt once and obeys it for the rest of the run. Rules are read for the product token openrows, falling back to the * group when the file does not name us, with * and $ wildcards and the standard "longest matching rule wins" precedence. A Crawl-delay set for us is honoured, up to 10 seconds. When a page redirects, the URL the redirect actually leads to is checked again against its own host's rules before anything is read from it, so a redirect cannot carry the Actor onto a site, or a path, that its owner puts off limits.

  • Disallowed URLs are never requested. They are reported under skipped.robotsDisallowed in the run summary, so you can see exactly how many of your input URLs the site puts off limits.
  • No robots.txt (404 or 410) means no rules, and the host is crawled normally. It is counted as robotsAbsent in the summary.
  • A robots.txt we cannot read means the host is skipped, not crawled. Any other response — 401, 403, 429, a 5xx, a timeout, a network failure, or a 200 that turns out to be an error or login page — leaves us without the site's rules, and this Actor will not guess. Every URL on that host is skipped and counted under skipped.robotsUnknown, and the host and the status that stopped us are listed under robots.unknownHosts in the summary. If a host you supplied returns no results, look there first.

Removal requests. If you believe this Actor's output is wrong or exposes something it should not, open an issue on the Actor's Issues tab and state the URL(s) concerned.

You are responsible for using this Actor, and the pages it fetches on your behalf, in line with the terms of each site you point it at and the laws that apply to you.

Third-party data: the fingerprint database

Technology detection is powered by a curated subset of the WebAppAnalyzer technology fingerprint database (https://github.com/enthec/webappanalyzer), the community-maintained continuation of the original Wappalyzer technologies.json (Wappalyzer's client went closed-source in August 2023; the fingerprint data was continued as an open project). It is used here under its GPL-3.0 license, vendored (never fetched from a third party at run time) with full attribution and the license text included in this Actor's source. Full provenance, license reasoning and the exact field subset kept: see THIRD_PARTY_NOTICES.md and FINGERPRINTS_LICENSE.txt in this Actor's source.

Licence

This Actor's own source code is available under the GNU General Public License v3.0 (GPL-3.0) — see the LICENSE file in this Actor's source for the full text. It is licensed this way because it vendors fingerprint data from the WebAppAnalyzer project (https://github.com/enthec/webappanalyzer), which is itself GPL-3.0-licensed; see "Third-party data" above and THIRD_PARTY_NOTICES.md for the full provenance and license reasoning.

Support

Report bugs and request fields on the Issues tab of this Actor. Include the run ID.