sfoln/tech-stack-detector · Lead generation, SEO tools, Developer tools
Detect the tech stack of any website: CMS, e-commerce platform, analytics, tag managers, ad pixels, chat, payments, CDN, hosting and web server, plus SaaS verified in DNS (Google Workspace, HubSpot, Zendesk…). Live detection, one JSON record per site. $2 per 1,000 websites.
Find out what any website is built with: give it domains or URLs and get one JSON record per site with every detected technology (name, categories, version, confidence and the evidence it was found by) plus headline fields: CMS, e-commerce platform, analytics, tag managers, ad pixels, live chat, payment processors, CDN, hosting, web server and frameworks. On top of the page itself it reads the TLS certificate and DNS records, so it also finds SaaS that never shows up in the HTML: the mailbox provider (Google Workspace, Microsoft 365), e-mail senders from SPF (SendGrid, Mailchimp, Amazon SES), and services verified in TXT records or behind subdomains (HubSpot, Zendesk, Stripe, Atlassian…). Built for sales prospecting by technology ("Shopify stores using Klaviyo"), competitive research, lead enrichment and AI agents.
Price
Price per delivered record
Event
Price
Per 1,000
Charged for
Website tech stack tech-stack
$0.002
$2 / 1,000
Charged once per website whose start page answered: detected technologies with categories, versions, confidence and evidence, headline fields (CMS, e-commerce platform, analytics, CDN, hosting, mailbox provider…) and SaaS seen in DNS.
Apify free plan: up to 10 items per run. Paid plans have no per-run cap; set maxTotalChargeUsd to cap spend.
POST https://api.apify.com/v2/actors/sfoln~tech-stack-detector/runs?maxTotalChargeUsd=1
GET https://api.apify.com/v2/actor-runs/{runId} # until status is SUCCEEDED
GET https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json
Authenticate with an Apify API token. MCP clients can instead sign in with OAuth when prompted. Example prompt for an agent: Which CMS, e-commerce platform, analytics tools and e-mail provider do apify.com and wordpress.org use?
Input
Input fields
Enter websites (domains or page URLs). Each website produces one dataset record with its detected technologies.
Field
Type
Default
Meaning
urls *
array
required
Websites Domains (apify.com) or page URLs (https://www.apify.com/pricing), one per line. A domain is analysed from its homepage (https://, falling back to http://). Duplicates are removed. Accounts on the Apify free plan can process up to 10 websites per run.
maxPages
integer
1
Pages per website 1 = the start page only; 2 = also one more page of the same site (pricing, shop, blog, contact…) to catch technologies that load only there. Range 1–2.
includeDnsSaaS
boolean
true
Detect SaaS from DNS Read TXT verification tokens, MX, SPF, NS and the CNAMEs of common subdomains (help., shop., status., …) to find services that never appear in the HTML: Google Workspace, Microsoft 365, HubSpot, Zendesk, Stripe, SendGrid and more.
categories
array
—
Only these categories Keep only technologies in these categories, e.g. CMS, Ecommerce, Analytics, Advertising, CDN, Payment processors, Marketing automation (case-insensitive). Empty = every category.
minConfidence
integer
0
Minimum confidence Drop technologies detected with a confidence below this value (0–100; most detections are 100). Range 0–100.
maxItems
integer
1000
Max websites Safety cap on how many websites are processed in this run. Range 1–10000.
concurrency
integer
5
Concurrency How many websites are analysed in parallel (1–10). Range 1–10.
includeRaw
boolean
false
Include raw response data Attach the response headers of the analysed pages and the DNS records used under raw (larger output).
Example input
{
"urls": [
"apify.com"
]
}
Output
One record per input item
Records land in the run's dataset; a synchronous call returns them directly. Each has a status (ok is charged; everything else is free) and a charged flag. The run's OUTPUT record totals items and charges.
Field
Type
Meaning
input
string | object
Input The website as submitted.
status
string
Status ok | not_found | error | skipped_invalid | skipped_budget | skipped_free_plan | skipped_trial | skipped_unmetered e.g. "ok"
charged
boolean
Charged Whether this record incurred a pay-per-event charge.
chargedEvent
string
Charged event Pay-per-event event charged for this record (present only when charged).
chargedCount
integer
Events charged Events actually charged when the charge came back short of the requested count (present only then; a warning explains it).
sponsored
boolean
Sponsored true when a promotional trial paid for this record instead of you: delivered with charged false and never billed (present only then).
promotionId
string
Promotion Id of the promotional trial that sponsored this record (present only with sponsored).
trialUnitsApplied
integer
Trial events Events of this record the trial paid for (present only with sponsored).
retailValueWaivedUsd
number
Value waived (USD) What those events cost at the listed prices, in USD: what you did not pay (present only with sponsored).
source
string
Source website+dns: the site's own pages, its TLS certificate and public DNS.
fetchedAt
string
Fetched at ISO timestamp.
error
object
Error { code, message, provider?, status? } when status is not ok. Codes: site_blocked (401/403/429/503), page_not_found (404/410), http_error, domain_not_found, invalid_url, network, timeout, tls_error, unexpected_type, too_many_redirects; runtime codes: budget_exhausted, free_plan_limit, unmetered, aborted, charge_failed, record_too_large, invalid_record, dataset_push_failed; promotional-trial codes (status skipped_trial; skipped_free_plan when the free-plan limit of the paid data source is reached or cannot be checked): allowance_exhausted, run_cap, campaign_cap, actor_cap, provider_cap, portfolio_cap, campaign_inactive, window_closed, halted, not_eligible, unknown_cost, ledger_unavailable, invalid_request, free_plan_subsidy_cap.
warnings
array
Warnings Notes about this record (second page or DNS lookups failed, page cut at 2 MB, detection time budget reached).
domain
string | null
Domain Registrable domain of the website. e.g. "apify.com"
url
string | null
URL Final URL of the analysed start page (after redirects).
httpStatus
integer
HTTP status Status of the start page when it answered with an error (not_found / error records).
title
string | null
Page title
language
string | null
Language The page's lang attribute.
technologyCount
integer
Technologies
technologyNames
array
Technology names Most specific category first, then by confidence.
byCategory
object
By category Category name → technology names, e.g. { "CMS": ["WordPress"], "Analytics": ["Google Analytics"] }.
cms
string | null
CMS
ecommercePlatform
string | null
E-commerce platform
analytics
array
Analytics
tagManagers
array
Tag managers
advertising
array
Advertising and retargeting
marketingAutomation
array
Marketing automation
crm
array
CRM
liveChat
array
Live chat
paymentProcessors
array
Payment processors
emailProvider
string | null
Mailbox provider From the MX records (Google Workspace, Microsoft 365, …).
emailSenders
array
E-mail senders Services allowed to send the domain's mail (SPF includes).
hosting
array
Hosting / PaaS / IaaS
cdn
array
CDN
webServer
string | null
Web server
javascriptFrameworks
array
JavaScript frameworks
programmingLanguages
array
Programming languages
saasVerifiedByDns
array
SaaS seen in DNS Technologies found in DNS records (verification tokens, MX, SPF, NS, CNAME).
The Actor loads the page like a browser would (without running JavaScript) and matches its response headers, cookies, meta tags, script URLs, inline scripts and HTML against more than 3,500 technology fingerprints (Wappalyzer 6.10.54, MIT licence, plus our own rules for newer frameworks and tag snippets). It also reads the TLS certificate issuer and, with includeDnsSaaS, DNS records: verification tokens, mail servers, SPF senders, name servers and the CNAMEs of help., shop., status. and similar subdomains.
What can it miss?
Technologies that only load after JavaScript runs in a browser (some tag-manager payloads, single-page apps that inject everything client-side) and back-end tools that leave no trace in pages or DNS. Sites behind bot protection that refuse the request are returned with status not_found and are not charged.