Detect technologies exposed by public websites from a list of domains or URLs. The Actor inspects the homepage response once and combines headers, cookie names, metadata, script sources, stylesheet links, and HTML markers into one row per detected technology.
Use it for lead enrichment, migration research, martech audits, SEO reviews, and competitor monitoring. It is an independent detector: it does not scrape or query a third-party technology database.
What does Website Technology Stack Detector do?
- 60+ curated detections across CMS, ecommerce, analytics, advertising, marketing, support, payments, frameworks, hosting, CDN, web servers, and consent tools
- deterministic confidence, safe evidence labels, and versions when a public signal exposes one
- validated redirects, bounded retries, anti-bot challenge detection, and private-network protection
- resumable progress for larger lists, with completed in-flight domains recovered from the dataset to prevent duplicate item charges
- one dataset row per technology, plus free status rows for no-match and request-error outcomes
Who is it for?
- sales and growth teams enriching lead lists with visible platform signals
- agencies comparing client and competitor website stacks
- developers routing domains by framework, hosting, or infrastructure
- ecommerce and martech teams auditing storefront, payment, analytics, and support tools
Input settings
| Field | Description |
|---|---|
domains |
Required public domains or HTTP(S) URLs. Bare domains try HTTPS first. |
maxDomains |
Safety cap after duplicate targets are removed; default 3. |
includeCategories |
Optional multiselect category filter. |
maxRunSeconds |
Processing budget from 45 to 300 seconds; default 300. |
resumeFromCheckpoint |
Resume matching work from the same default key-value store; default true. |
proxyConfiguration |
Optional proxy settings; direct connections are the default. |
Example input
{
"domains": ["wordpress.org", "shopify.com", "vercel.com"],
"maxDomains": 3,
"includeCategories": ["CMS", "Ecommerce", "Hosting"]
}
Credentials, custom ports, IP literals, single-label/local hosts, and redirects to private or reserved networks are rejected.
Example output
{
"domain": "wordpress.org",
"normalizedDomain": "wordpress.org",
"sourceUrl": "https://wordpress.org/",
"finalUrl": "https://wordpress.org/",
"statusCode": 200,
"redirectCount": 0,
"pageTitle": "Blog Tool, Publishing Platform, and CMS – WordPress.org",
"technologyName": "WordPress",
"category": "CMS",
"subcategory": "Content management system",
"description": "Open-source content management system.",
"vendorUrl": "https://wordpress.org/",
"confidence": 96,
"version": null,
"evidenceType": "link",
"evidenceDetail": "WordPress asset path",
"sourceSection": "link: WordPress asset path",
"firstDetected": null,
"lastDetected": null,
"scrapedAt": "2026-07-20T12:00:00.000Z"
}
confidence is the strength assigned to the matched rule, not a statistical probability. firstDetected and lastDetected remain nullable compatibility fields because a live passive scan has no historical database. Raw cookies, headers, page snippets, IDs, and keys are never copied into evidence fields.
How detection works
The Actor downloads the public page HTML without executing JavaScript. It evaluates several passive signal families and keeps the strongest deterministic match for each technology. Framework relationships can add an implied row—for example, a strong Next.js signal can imply React—unless a stronger direct React signal already exists.
This bounded approach is faster and less failure-prone than launching a browser for every domain. It also means technologies loaded only after interaction, consent, client-side network calls, login, or bot checks may not be visible. GraphQL is detected when an Apollo Client signal is present; the Actor does not probe arbitrary /graphql endpoints because unrelated websites do not share a safe, stable GraphQL contract.
API usage
Node.js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('fetch_cat/builtwith-domain-tech-stack-scraper').call({
domains: ['wordpress.org', 'shopify.com'],
maxDomains: 2,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
Python
from apify_client import ApifyClient
import os
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('fetch_cat/builtwith-domain-tech-stack-scraper').call(run_input={
'domains': ['wordpress.org', 'shopify.com'],
'maxDomains': 2,
})
print(client.dataset(run['defaultDatasetId']).list_items().items)
cURL
curl "https://api.apify.com/v2/acts/fetch_cat~builtwith-domain-tech-stack-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"domains":["wordpress.org","shopify.com"],"maxDomains":2}'
MCP and automation
Use the Actor through Apify MCP at:
https://mcp.apify.com/?tools=fetch_cat/builtwith-domain-tech-stack-scraper
Claude Code setup:
claude mcp add apify-tech-detector "https://mcp.apify.com/?tools=fetch_cat/builtwith-domain-tech-stack-scraper"
Claude Desktop or another compatible client can use:
{
"mcpServers": {
"apify-tech-detector": {
"url": "https://mcp.apify.com/?tools=fetch_cat/builtwith-domain-tech-stack-scraper"
}
}
}
Example agent prompts:
- “Detect ecommerce and payment technologies for these domains.”
- “Compare the CMS and hosting stack across this competitor list.”
- “Return domains where HubSpot, Klaviyo, or Intercom is visible.”
For recurring monitoring, save the input as an Apify Task and schedule it. A task that needs to continue a checkpoint must reuse the same default key-value store.
Support
If a public website produces an unexpected result, open an Actor issue and include the Apify run ID or run URL, the input JSON, expected output, actual output, and one reproducible public URL. Remove tokens, credentials, customer data, and private URLs before posting.
Responsible use
Use the Actor only on public websites and comply with applicable terms, laws, and outreach rules. Keep request volume reasonable. Technology signals can be incomplete or stale, so validate consequential decisions with the website owner or another authorized source.