Tech Stack Audit — Website Technology Detector with Evidence avatar

Tech Stack Audit — Website Technology Detector with Evidence

Pricing

from $35.00 / 1,000 site audits

Go to Apify Store
Tech Stack Audit — Website Technology Detector with Evidence

Tech Stack Audit — Website Technology Detector with Evidence

Detect a website's tech stack with a real browser: CMS, ecommerce, frameworks, CDN, analytics and pixels. Returns the version and how old it is, which trackers really fire, security headers, cookies set before consent — and the evidence for every detection.

Pricing

from $35.00 / 1,000 site audits

Rating

0.0

(0)

Developer

Power On Labs

Power On Labs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Tech Stack Audit — website technology detector that shows its work

Point it at a website and get back its technology stack: CMS, ecommerce platform, JavaScript framework, web server, CDN, analytics, advertising pixels, consent platform, payment provider, and about 280 technologies in all.

Then it answers the three questions a plain technology list leaves open:

  1. Which version, and how old is it? Not "WordPress", but WordPress 6.4.3, on a line that shipped 2.8 years ago, 6.8 is newer.
  2. What actually runs? Not "the Meta Pixel tag is in the HTML", but the Meta Pixel really fired a network request on page load — or didn't.
  3. Is the site's own house in order? Security headers present, missing or weak, and which tracking cookies were written before anyone consented.

And for every single detection, the evidence: the exact header, script URL, cookie, meta tag, JavaScript global or DOM node that identified it.

A BuiltWith and Wappalyzer alternative that runs a real browser instead of reading the HTML and guessing.


Why the evidence matters

Every competing tool returns a list of names. A name you cannot check is a name you have to trust, and technology detection is guesswork often enough that trust is the wrong setting. So every technology in the output carries what proved it:

{
"name": "jQuery",
"version": "3.6.0",
"confidence": 100,
"activeAtRuntime": true,
"evidence": [
{ "type": "javascript global", "source": "window.jQuery — jQuery.fn.jquery = 3.6.0", "matched": "window.jQuery" },
{ "type": "resource", "source": "https://www.php.net/js/vendor/jquery-3.6.0.min.js", "matched": "jquery-3.6.0", "seenIn": "markup and network" }
],
"versionAge": {
"releasedOn": "2021-03-02",
"ageDays": 2014,
"latestKnown": "3.7",
"outdated": true,
"verdict": "jQuery 3.6.0 is on the current major line but line 3.6 shipped 5.5 years ago; 3.7 is newer."
}
}

You can verify that line yourself in ten seconds. That is the whole idea.

What a real browser buys you

Most technology lookups fetch the HTML and read the headers. That is fast and cheap, and it cannot see:

QuestionHTML-only scannerThis Actor
Is the Meta Pixel on this page?It's in the markupIt's in the markup and it fired — or it's in the markup and never fired
Which third parties get visitor data?The ones written into the pageEvery domain the browser really contacted — 22 on a typical news site
Which cookies are set before consent?Cannot know: no cookies are set without executing the pageThe full list, classified, with lifetimes
Which tags a consent banner is actually blockingInvisibleReported as declared but not requested

The audit clicks nothing — no banner is accepted, no button is pressed. That constraint is what lets the privacy section say "before consent" and mean it.

Sample output, one row per site

{
"url": "https://example.com",
"reachedRealPage": true,
"statusCode": 200,
"technologyCount": 11,
"technologyNames": ["WordPress", "jQuery", "Google Tag Manager", "Meta Pixel", "..."],
"categories": { "CMS": ["WordPress"], "Advertising": ["Meta Pixel"], "...": [] },
"cms": { "name": "WordPress", "version": "6.4.3", "outdated": true, "verdict": "…2.8 years ago…" },
"jsFramework": { "name": "React", "version": null, "outdated": null, "verdict": null },
"webServer": { "name": "nginx", "version": "1.18.0", "outdated": true, "verdict": "…" },
"outdatedTechnologies": [ { "name": "PHP", "version": "7.4.33", "endOfLife": true, "verdict": "…no longer receives security fixes…" } ],
"outdatedCount": 2,
"endOfLifeCount": 1,
"runtime": {
"requestCount": 148,
"thirdPartyDomainCount": 22,
"technologiesActiveAtRuntime": ["Google Tag Manager", "Meta Pixel"],
"technologiesDeclaredButNotRequested": ["Comscore"],
"trackersFiring": ["Google Tag Manager", "Meta Pixel"]
},
"security": {
"grade": "D", "score": 47, "https": true,
"headersMissing": [ { "label": "HSTS", "cost": 18, "why": "Without HSTS a first visit over http can be intercepted…" } ],
"headersWeak": [ { "label": "Content-Security-Policy", "warning": "Policy present but weak: allows 'unsafe-inline', allows any host (*) for scripts." } ],
"softwareDisclosure": [ { "header": "x-powered-by", "value": "PHP/7.4.33", "versionLeak": true } ],
"scoring": "Starts at 100. Each missing header costs its listed weight…"
},
"privacy": {
"consentPlatformDetected": true,
"consentPlatforms": ["OneTrust"],
"trackersActiveBeforeConsent": ["Google Tag Manager"],
"trackingCookiesBeforeConsent": [ { "name": "_fbp", "tracker": "Meta Pixel", "expiresInDays": 89 } ],
"finding": "Consent platform present (OneTrust), but 1 tracker(s) and 1 tracking cookie(s) were already active before any choice was made."
},
"technologies": [ /* every detection, with its evidence */ ],
"thirdPartyDomains": ["connect.facebook.net", "…"]
}

What it's for

  • Lead qualification — find every prospect running Shopify, or WooCommerce, or an end-of-life PHP.
  • Competitive research — what a competitor's stack is, and how current.
  • Agency pitches — walk in with "your WordPress is two years old, your PHP is end-of-life, and you have no CSP", each line backed by the header that proves it.
  • Privacy and GDPR review — which trackers fire before consent, on your sites or a supplier's.
  • Security triage at scale — a grade and a missing-header list per domain.

Input

Paste a list of websites. Bare domains are fine — example.com becomes https://example.com.

FieldDefaultWhat it does
urls["https://example.com"]Websites to audit, one row of output per site
devicedesktopdesktop or mobile — some sites serve a different stack to phones
minConfidence50Raise to 80 for detections proven by a decisive signal only
includeEvidencetrueAttach the proof to every detection
includeCookiestrueFull cookie list, with lifetimes and tracker attribution
includeThirdPartyDomainstrueEvery external domain really contacted
includeRequestLogfalseEvery network request, for auditing the evidence yourself
waitUntilloadload, domcontentloaded or networkidle
settleMs1500Extra wait so late-firing tags are still caught
timeoutSecs45Per site
concurrency4Sites audited in parallel
retries1Retries before a site is recorded as failed
locale / timezoneen-US / —Changes which consent banner and regional tags load

Pricing

$0.05 per site audited — half what the two leading tech-stack Actors charge for a lookup that never opens a browser. No start fee, no subscription, no API key. Higher Apify plans pay less, down to $0.035.

You are not charged for:

  • sites that failed to load — DNS failure, timeout, connection refused, TLS error;
  • anything that answered 4xx or 5xx instead of a page;
  • sites behind a bot wall, where the real page was never reached (see below).

Every row says which it was, in a charged field.

Read before you buy — two honest limits

1. Bot walls are reported, not defeated. Some sites answer a bot-detection challenge instead of their page: Cloudflare, Fastly, DataDome, Imperva, Akamai, AWS WAF, PerimeterX. This Actor recognises the wall, sets

reachedRealPage: false
, names the vendor, and does not charge you:

{ "reachedRealPage": false,
"blockedBy": { "vendor": "Fastly", "marker": "Fastly client challenge",
"explanation": "…the technologies below describe the wall, not the site…" },
"charged": false }

Getting past those walls reliably needs paid residential proxies. This Actor does not use them. A scanner that meets a challenge page and cheerfully reports "Cloudflare, nginx" has audited the wall, not the site, and given you no way to tell — which is worse than saying so. If you have your own proxies, pass them in the proxyConfiguration field.

2. The version table has a compile date, and it ships with every result. Version age is judged against a bundled table of release lines and dates; the date it was compiled is in every row as versionTableCompiledOn. If a site runs something newer than the table knows, it is reported as newer than the table, never as outdated. Where a version cannot be established the output says ageDays: null and basis: "unknown" rather than inventing a verdict.

Coverage

About 280 technologies across 42 categories: CMS, ecommerce, headless CMS, website builders, page builders, JavaScript frameworks and libraries, UI frameworks, static site generators, build tools, analytics, session recording, tag managers, A/B testing, advertising and pixels, marketing automation, email marketing, customer support and chat, consent management, CDN, hosting, reverse proxies, web servers, programming languages, web frameworks, databases, security and bot management, search, payments, reviews, video, media, maps, fonts, authentication, backends, forms, scheduling, comments, monitoring and SEO.

Detection rules are written and maintained for this Actor. Version age is tracked for the technologies that realistically expose a version: WordPress, Drupal, Joomla, TYPO3, PHP, jQuery, Bootstrap, nginx, Apache, IIS, React, Next.js, Vue, Angular, AngularJS, Magento, PrestaShop, Shopware, Django, Laravel, Symfony, Rails, Ember, Font Awesome, Moment.js, Lodash, D3, GSAP, Video.js, Swiper, Alpine.js and htmx.

Use it from code

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('power_on/tech-stack-audit').call({
urls: ['https://example.com', 'https://another-site.com'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const site of items) {
if (!site.ok || !site.reachedRealPage) continue;
console.log(site.url, site.technologyNames.join(', '));
for (const old of site.outdatedTechnologies) console.log(' ⚠', old.verdict);
}
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("power_on/tech-stack-audit").call(run_input={
"urls": ["https://example.com", "https://another-site.com"],
})
for site in client.dataset(run["defaultDatasetId"]).iterate_items():
if not site.get("ok") or not site.get("reachedRealPage"):
continue
print(site["url"], site["technologyNames"])
for old in site["outdatedTechnologies"]:
print(" !", old["verdict"])

Also from Power On Labs