Website Tech Stack Detector: CMS, Versions & CDN, No Login avatar

Website Tech Stack Detector: CMS, Versions & CDN, No Login

Pricing

from $1.50 / 1,000 site checkeds

Go to Apify Store
Website Tech Stack Detector: CMS, Versions & CDN, No Login

Website Tech Stack Detector: CMS, Versions & CDN, No Login

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.

Pricing

from $1.50 / 1,000 site checkeds

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 days ago

Last modified

Share

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

Table of contents

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 jobInput pathOutput entityKey fieldsNext integration
Build a lead list by platformurls (domains from your CRM)One row per siteurl, ecommerce, cms, technologyCountExport CSV, filter, import to CRM
Competitor or client auditurls + useBrowser: trueFull technology listtechnologies[].name, version, categoriesReport or spreadsheet
Check one category onlyurls + categories: ["Analytics"]Filtered technology listtechnologies, analyticsGoogle Sheets
Regular stack checkurls on a scheduleRow with datedetectedAt, cms, cdnCompare runs over time
AI agent questionurls (MCP)One row per sitecms, ecommerce, analytics, cdn, serverAgent answer

Input

Input fields

FieldPlain nameTypeRequiredDefault / prefillAllowed valuesEffectExampleRecommendation
urlsWebsitesarray of stringsyesprefill ["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
useBrowserRender JavaScript in a browserbooleannodefault falsetrue, falseOpens each page in headless Chrome to also find JavaScript-only technologiestrueKeep off for big lists; turn on for a detailed audit
categoriesOnly these categoriesarray of stringsnodefault [] (all)names from the list in the Input tab, e.g. CMS, Ecommerce, Analytics, CDNKeeps only these categories in technologies["CMS", "Ecommerce"]Use when you need one kind of technology. The summary columns are always filled
maxConcurrencyParallel sitesintegernodefault 101–20 (browser mode: at most 5)How many sites are checked at the same time20The 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

WhenInput AInput BEffective behaviorExampleCost/result effect
Browser modeuseBrowser: truemaxConcurrencyAt most 5 pages at the same timemaxConcurrency: 20 → 5Browser price per site; more memory
Category filtercategoriessummary columnstechnologies is filtered; cms, ecommerce, analytics, cdn, server, framework, payment are always filled["Web servers"]No price change
Big listsurls > 20 (HTTP) or > 10 (browser)default memoryThe run gets more memory (1 GB for HTTP, 2 GB for browser)500 domainsSee Pricing for the start charge per GB

Input examples

Quick check of a domain list

{
"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

{
"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

{
"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):

{
"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:

{
"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

FieldMeaningTypeCan be empty?Example / enumSource / notes
urlYour input linestringnowordpress.orgAs given
finalUrlPage URL after redirectsstring or nullnull if no page was receivedhttps://wordpress.org/
statusRow resultstringnocomplete, error
httpStatusHTTP status of the final pageinteger or nullyes200, 404
technologiesTechnologies foundarray of objects[] if none{name, categories, version, confidence, website}Filtered by categories if set
technologies[].versionVersion, if the page shows itstring or nulloften null7.2From meta tags, script URLs, headers
technologies[].confidenceHow sure the match isintegerno1000–100, from the fingerprint set
technologyCountNumber of technologies in the listintegerno13
cmsCMS namesarray of strings[] if none["WordPress"]Always from all categories
ecommerceEcommerce platformsarray of strings[] if none["Shopify"]
analyticsAnalytics toolsarray of strings[] if none["Leadfeeder"]
cdnCDNsarray of strings[] if none["Cloudflare"]
serverWeb servers and reverse proxiesarray of strings[] if none["Nginx"]Frameworks are in framework
frameworkWeb and JavaScript frameworksarray of strings[] if none["Next.js"]
paymentPayment processorsarray of strings[] if none["Stripe"]
modeHow the page was loadedstringnohttp, browser
pageTitlePage <title>string or nullyes
detectedAtWhen the page was checkedstringnoISO 8601 UTC
fingerprintsVersionFingerprint data usedstringnoenthec/webappanalyzer@eea872a
fingerprintsLicenceLicence of the fingerprint datastringnoGPL-3.0-only
errorWhy the site could not be checkedobject or nullnull 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

SituationDataset/API/KVSResult shapeUser action
Normal itemDatasetcomplete row with technologies and summary columnsFilter, sort, export
Page checked, nothing foundDatasetcomplete row, technologies: []The page has no known signals; try another page of the site or browser mode
Site could not be checkedDataseterror row with error.codeSee Troubleshooting
Bad input linesRUN_SUMMARYinvalidSkipped, invalidSamplesFix those lines and run them again
Run stopped earlyRUN_SUMMARY + status messagestatus: "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.

EventWhenFree planBronzeSilverGold and higher
Actor startOnce 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.

ScenarioInput scopeCharged sitesStartSite chargesTotal
Prefill3 domains, HTTP3$0.007$0.0054$0.0124
100 domainsHTTP, 1 GB100$0.007$0.18$0.187
1,000 domainsHTTP, 1 GB1,000$0.007$1.80$1.807
Browser audit5 sites, browser, 1 GB5$0.007$0.0175$0.0245
Browser list100 sites, browser, 2 GB100$0.014$0.35$0.364
Invalid inputno 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 (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.
  • 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 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).
  • 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.