Website Tech-Stack Detector — Find Any Site's Technologies
Pricing
from $1.45 / 1,000 domain-results
Website Tech-Stack Detector — Find Any Site's Technologies
Detect technology stacks from target domain responses. Get technologies, confidence, evidence, normalized URLs and HTTP data. Run with domains or URLs; no external credentials. Pricing: $0.0015 per dataset item on Free; Actor Start: $0.0005 per event.
Pricing
from $1.45 / 1,000 domain-results
Rating
0.0
(0)
Developer
Vitalii Bondarev
Maintained by CommunityActor stats
0
Bookmarked
37
Total users
12
Monthly active users
a day ago
Last modified
Categories
Features
Share
BuiltWith Tech Stack Scraper
Scrape a domain's live web response for detected CMS, ecommerce, frameworks, analytics, CDN and payment technologies, with confidence and evidence. On the Free plan, each priced dataset item costs $0.0015 per domain result; a run handles at most 10,000 domains.
- Extract ecommerce, CMS and marketing signals for lead generation and sales prospecting.
- Compare competitor stacks across scheduled runs using timestamps and detected technologies.
- Analyse technology distribution across an industry or company list.
- Inspect confidence scores and the strongest matched signal for each detection.
- Run without a buyer API key, external credentials or external proxy account.
What you get
The Actor fetches each listed domain live and returns normalized URLs, HTTP details, detected technologies, confidence values and evidence.
| Field | Type | Description |
|---|---|---|
domain | string | Normalized domain without www. or a protocol. Always set. |
url | string | URL requested for the normalized domain. Always set. |
final_url | string | Final URL after redirects; null when no response was received. |
status_code | integer | HTTP status code; null when no response was received. |
techs | array of objects | Detected technologies, or [] when none are found or the row has an error. The ruleset has 168 entries, matched on headers, cookie names, meta generator, script URLs, inline JavaScript or HTML patterns. |
techs[] | object | Contains name, tag, categories, link, confidence, evidence and version. |
techs[].name | string | Display name of the detected technology. |
techs[].tag | string | Primary category tag. |
techs[].categories | array of strings | Category labels assigned to the technology. |
techs[].link | string | Reference URL in the form https://builtwith.com/<slug>. |
techs[].confidence | number | Detection confidence. |
techs[].evidence | string | Strongest signal: header:<header name in lower case>, cookie:<cookie name, first 40 characters>, meta-generator:<generator text, first 40 characters>, script-src:<script URL, first 60 characters>, inline-js or html. |
techs[].version | string | Technology version, or null unless the winning pattern captures one. |
tech_count | integer | Number of detected technologies; 0 when none are found. |
categories | array of strings | Union of category labels; [] when none are found. |
parse_confidence | number | Average confidence rounded to 3 decimals; 0.0 when none are found. |
error | string | null on a good row; otherwise fetch_failed: <detail>, access_challenge or unusable_response. |
warnings | array of strings | [] on a good row; otherwise ["fetch_failed"], ["access_challenge"], ["unusable_response"] or ["no_tech_detected"]. |
status | string | Present only when no technologies are found: no_technologies_detected. |
scraped_at | ISO 8601 string | UTC timestamp shared by every row in a run. |
When the source provides no value, final_url, status_code, error and techs[].version are null, subject to the table exceptions. Header or meta-generator matches score 0.95; HTML-only matches score 0.72.
Sample output
The sample below is one real record from the dataset sample.
[{"domain": "github.com","url": "https://github.com/","final_url": "https://github.com/","status_code": 200,"techs": [{"name":"GitHub Pages","tag":"Hosting","categories":["Hosting","PaaS"],"link":"https://builtwith.com/GitHub-Pages","confidence":0.95,"evidence":"header:server","version":null},{"name":"Contentful","tag":"CMS","categories":["CMS","Headless CMS"],"link":"https://builtwith.com/Contentful","confidence":0.72,"evidence":"html","version":null}],"tech_count": 2,"categories": ["Hosting","PaaS","CMS","Headless CMS"],"error": null,"parse_confidence": 0.835,"warnings": [],"scraped_at": "2026-10-29T11:18:17.331912+00:00"}]
Pricing
Billing is pay per event. apify-actor-start costs $0.0005 per event on the Free plan and is charged when the Actor starts. apify-default-dataset-item costs $0.0015 per event on the Free plan and represents one result with all fields. A result is chargeable when it has no error and at least one detected technology; failed or no-technology rows remain visible without a result charge.
The domain-result event has no price, so you are not charged for it. The apify-actor-start event is charged once per GB of run memory, with a minimum of one event; a run with more than 1 GB starts more events. A one-result run shows the exact count under charged events.
Example: 1,000 priced dataset items and one minimum apify-actor-start event cost $1.5005 on the Free plan. For 10,000 priced dataset items and one minimum apify-actor-start event, the cost is $15.0005 on the Free plan.
The Apify Free plan includes $5 of monthly platform usage credit. With exactly one start event, the Free-plan $5 credit covers 3,333 priced dataset items, calculated as floor((5 - 0.0005) / 0.0015).
You pay only for the events listed below. This Actor does not use Pay per event + usage, so compute, proxy traffic and storage are not billed separately.
| Plan | apify-actor-start | apify-default-dataset-item |
|---|---|---|
| Free | $0.0005 per event | $0.0015 per event |
How to use
- Prepare bare domains or full URLs, such as
shopify.comorhttps://www.example.com/path. - Configure
maxItemsto cap processing, or leave it at0for no input cap. - Keep the default Apify residential proxy configuration, or provide an accepted Apify proxy configuration.
- Run the Actor with this input:
{"startDomains": ["shopify.com", "https://www.example.com/path", "stripe.com"],"maxItems": 0,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
- Inspect
techsfor names, categories, links, confidence and evidence. - Filter by
techs,categories,status_codeorparse_confidence, then export JSON or CSV. - Repeat runs through Apify Schedules; weekly is a starting point for competitor-stack monitoring.
Integration: read the results through the Apify dataset API.
Input parameters
| Parameter | What it does | Default |
|---|---|---|
startDomains | Required domains or URLs. Inputs are normalized, duplicates removed, and rows containing spaces or commas split into domains. | Required |
maxItems | Caps listed domains to process. 0 means no cap and cannot create domains. | 0 |
proxyConfiguration | Proxy configuration used to reach target sites. | Apify RESIDENTIAL |
disableUsageStats | Set to true to turn off the run summary in Usage statistics. | false |
Limits and edge cases
| Condition | What you see | What to do |
|---|---|---|
| More than 10,000 domains | Processing stops at 10,000; remaining domains are noted as unprocessed. | Run the remainder separately. |
| A domain appears more than once | Duplicate normalized domains are removed. | Supply distinct domains for separate tracking. |
| A row contains spaces or commas | Each piece becomes a domain. | Use separate pieces for separate entries. |
Entry has no dot or host, such as localhost | It is skipped. Up to 50 entries, each cut at 100 characters, appear as invalid_inputs in OUTPUT; the terminal message names the first 10. | Provide a domain such as example.com or a full URL. |
| Empty or whitespace-only row | Ignored silently. | Provide a valid domain or URL. |
| No valid entry | Run ends Failed with error: "no_valid_domains" in OUTPUT; nothing is read or charged. | Provide at least one valid domain. |
| Input list is empty | Run ends Failed with error: "no_domains" in OUTPUT. | Provide startDomains. |
maxItems is below the valid-domain count | Only the capped number is processed; remaining entries are noted. | Increase maxItems or split the list. |
| Every domain is fetched | Each is fetched directly first. | Residential retry occurs for status 401, 403, 407, 429, 451, 503 or 500+, a block page, or no technologies with a non-200 status or body under 5,000 characters. |
| Response is unusable, blocked or unreachable | Row contains fetch_failed, access_challenge or unusable_response; failed rows are pushed without a result charge. | Check the domain and proxy configuration, then retry later. |
| Page returns no technologies | A 200 response with a body of at least 5,000 characters and no technologies gets empty techs, tech_count 0 and no_technologies_detected; no result charge applies. | Review the URL and rerun if the site changed. |
| Short or non-200 response has no technologies | Row becomes unusable_response or access_challenge. | Check the domain and retry later. |
| Apify proxy is unavailable or setup fails | Run ends Failed before reading domains; OUTPUT has proxy_setup_failed or no_proxy; nothing is charged. | Enable Apify Proxy and check proxyConfiguration. |
| Cost guard stops the run | After residential proxy bytes or compute make continuation uneconomic, a partial run can end Succeeded with Partial result. OUTPUT has stopped_early: true, stoppedEarly: "MARGIN_GUARD", reason, domain_rows_delivered, domains_not_processed as a count, and message; a MARGIN_GUARD record has cost figures. With no delivered row, it ends Failed with nothing charged and the same stoppedEarly value. | Compare domain with the input; rerun unprocessed domains. |
| Rows are processed | Rows are pushed in groups of 20 domains plus a final group; pushed rows are charged at push time. | Use existing dataset rows as the delivered portion. |
| Run aborts, times out or fails mid-run | Up to 19 processed but unpushed domains are lost and not charged. No resume progress is saved; only OUTPUT and MARGIN_GUARD are written. | Rerun the missing domains. |
| No row is delivered and a lookup fails | Run ends Failed with stoppedEarly: "ALL_LOOKUPS_FAILED" in OUTPUT; the dataset lists each domain with its error or status. | Retry later and review errors. |
| No failures but every row has no technology | Run ends Succeeded with 0 delivered rows and nothing charged; OUTPUT contains message and domains_no_technologies. | Review the domains and rerun if needed. |
| Ruleset does not recognize a technology | It is absent from techs; the ruleset has 168 entries. | Technologies outside the 168 entries are not reported. |
FAQ
How much does it cost to scrape a website domain?
On the Free plan, a priced dataset item costs $0.0015 per event and the minimum apify-actor-start charge costs $0.0005 per event. 1,000 priced dataset items and one minimum start event cost $1.5005 on the Free plan.
What happens when a domain returns an empty result?
A 200 response with a body of at least 5,000 characters and no technologies is written with empty techs, tech_count 0 and no_technologies_detected. It is not charged as a result.
Why is a domain not working?
The target is fetched directly first. An unusable direct response can be retried over the Apify residential proxy; the dataset records an error marker and available status information, and failed rows are not charged.
Is collecting and using this public data legal?
The Actor reads the target domain’s public web response. Whether collection and use are allowed depends on the source’s terms and applicable law for your use case, so review those requirements before running it.
Related actors
- Company Signal Stack — Firmographics + Jobs + Funding — add firmographics, jobs and funding data to company-domain analysis.
Usage statistics
This Actor creates a small, content-free summary at the end of each run, used only to monitor reliability and improve this Actor. No input values, credentials or scraped data are sent.
A copy of the exact record for your run is saved as USAGE_STATS in your own Apify key-value store.
To opt out, set disableUsageStats to true in the input. Nothing is sent then.