Tech Stack Lookup — CMS, Ecommerce & Analytics Detection
Pricing
from $3.00 / 1,000 successful lookups
Tech Stack Lookup — CMS, Ecommerce & Analytics Detection
Find out what a website is built on — CMS, ecommerce platform, JS framework, analytics, CDN, payments and more — from its own HTML and headers, no third-party API. Paste a list of domains, press Start. Charged only for sites where something is detected.
Pricing
from $3.00 / 1,000 successful lookups
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
You give this actor a list of websites. For each one, it fetches the live page, reads the HTML
and response headers, and tells you what the site is built on — CMS, ecommerce platform, JS
framework, analytics, tag manager, marketing tool, chat widget, CDN, host, payment processor, and
security service. No third-party lookup API and no stored database sit between you and the
answer: every check is a fresh fetch of the exact page you asked about, matched against a
hand-written signature list. The list covers 37 named technologies grouped into 11 categories —
cms, ecommerce, framework, analytics, tag-manager, marketing, chat, cdn, hosting,
payments, and security — and each row tells you exactly which of those matched, and why
(the Server header, the X-Powered-By header, or a generator meta tag, where present).
Who it's for
The accountable_eel catalogue sells company intelligence columns for outbound. Each actor takes a list of domains or company identifiers and returns one flat, stably-named row per input — firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and per-domain: a few tenths of a cent for a row that was actually found, and nothing for a miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists — VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No seat licence, no monthly minimum, no credit system to decode. This one detects a site's technology stack straight from its response headers and HTML — no separate lookup database, no DNS record involved.
Why this one
- Reads the page's own signal, not a database of it. Script tags, meta generator tags,
JS-variable and CSS-class fingerprints, and response headers (
Server,X-Powered-By,CF-Ray,X-Vercel-Id) — the same category of signal Wappalyzer and BuiltWith are built on, fetched fresh on every run instead of read from a stored snapshot. - 37 signatures across 11 categories, kept deliberately small. Every signature requires an asset path, a JS variable, or a CSS class — something only present if the page is actually built on that stack — not a loose brand-name text match. (stripe.com's own customer-logo carousel mentions "WooCommerce" in alt text; that alone doesn't trigger a match here.) High-precision over broad-but-noisy is the trade-off, and it's a deliberate one. See "vs. alternatives" below for what that trade-off costs you.
- Never charged for a miss. A site that blocks the request, doesn't match any signature, or isn't a valid domain/URL still gets a row explaining why — and costs nothing. See "Price" below for the found-row rate.
- One row per site or one row per technology, your choice. Turn on "One row per technology detected" in the Input tab and the same run produces one row per detected technology instead of one grouped row — you're still charged once per site either way.
- No proxies to configure, no headless browser needed. This is a plain HTTP fetch through Apify's default proxy; the page's raw HTML and headers are enough for every signature here.
What you get
Every row starts with the same five fields, then adds the technology fields you selected in "Which columns do you want?" (all included by default).
| Field | Type | Description |
|---|---|---|
query | text | The website you submitted, echoed back |
found | boolean | true if at least one technology was detected on this page |
status | text | OK on a match; NOT_FOUND, BLOCKED, BAD_FORMAT, or REQUEST_FAILED on a miss |
message | text | Plain-English reason for a miss — only present when found is false |
scrapedAt | ISO 8601 datetime | When the page was fetched |
detectedCount | number | How many technologies were detected on this page (default one-row-per-site output) |
detected | array | Every technology detected, as {"name": "...", "category": "..."} objects (default one-row-per-site output) |
name | text | One technology's name — populated when "One row per technology detected" is on, in place of detected |
category | text | That technology's category: cms, ecommerce, framework, analytics, tag-manager, marketing, chat, cdn, hosting, payments, or security — populated alongside name |
server | text | The page's Server response header, if present |
poweredBy | text | The page's X-Powered-By response header, if present |
generatorMetaTag | text | The content of <meta name="generator">, if the page has one |
Deselect any of the technology fields in the Input tab's column picker to drop them from every
row — query, found, status, message, and scrapedAt always stay.
Price
$5 per 1,000 websites, plus a $0.005 start fee. Misses (found:false) are never charged.
1,000 domains through this actor: ~$5.62 if every one is found ($0.00562 per found row at the FREE tier, plus a $0.00005 actor-start charge), less if some aren't and less again on a paid Apify plan. The same 1,000 rows through a credit-based enrichment platform: $80–$400. A site where nothing matches, or that blocks the request, is never billed — you only pay for a row that actually names a technology.
How to use
- In the Apify Console. Open the actor page and click Start — the
websitesfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~tech-stack-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"websites":["stripe.com","shopify.com"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
- Open the Input tab and paste your list of websites into "Websites to check" — one per line,
as a bare domain (
stripe.com) or a full URL (https://stripe.com). - Leave "Try it first" on for your first run — it caps the run at 5 sites so you can see the output shape before spending on the full list.
- Decide whether you want one row per site (default —
detectedholds the full list) or one row per technology (turn on "One row per technology detected"); either way you're charged once per site. - Optionally narrow the "Which columns do you want?" list if you only need a subset of fields, or set "Only keep rows containing" / "Skip rows containing" to filter by keyword.
- Turn "Try it first" off and press Start to run the full list.
Typical uses: qualifying a prospect list by platform before you pitch a platform-specific service (who's on Shopify vs. WooCommerce vs. a custom build), checking what analytics, CDN, and payments stack a competitor runs, or auditing a portfolio of sites you manage to confirm each one still runs the stack you expect and catch any that quietly migrated.
Input
{"websites": ["stripe.com","shopify.com"]}
One domain or URL per line. Accepted formats: stripe.com, https://stripe.com.
Sample output
By default you get one row per website, with every technology detected listed together (turn on "One row per technology detected" in the Input tab to get one row per technology instead):
| query | found | status | detectedCount | detected | name | category | server | poweredBy | generatorMetaTag | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|
| https://stripe.com | true | OK | 1 | [{"name":"Next.js","category":"framework"}] | nginx | 2026-08-26T06:00:20.288Z |
A real row, one-row-per-site mode:
| query | found | status | detectedCount | detected |
|---|---|---|---|---|
| shopify.com | true | OK | 3 | Shopify (ecommerce), React (framework), Cloudflare (cdn) |
A site nothing on the detection list matches gets a row with found: false and a status/
message explaining why, and is never charged.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~tech-stack-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"websites":["stripe.com","shopify.com"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~tech-stack-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"websites":["stripe.com","shopify.com"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~tech-stack-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"websites":["{{website}}"]}, mapping the row's website into the websites array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Tech Stack Lookup | Apify" — the agent will find and run this actor.
Tips
- Run "Try it first" on a handful of domains you already know the stack for, to sanity-check the detection against sites you can verify by hand before you run a full list.
- If you only care about one category — say, ecommerce platform — narrow "Which columns do you
want?" to
detectedCountanddetected, then filter on the category client-side; you're still charged the same per-site rate regardless of column count. - A
BLOCKEDstatus means the site returned a 403, 429, or 503 to the request — retrying later or lowering "Max concurrency" sometimes gets through; a site that blocks automated traffic consistently will keep coming backBLOCKED. - Turn on "One row per technology detected" when you're loading results into a table you plan to
filter or group by category — it's easier to query
category = 'cms'across many rows than to parse a nesteddetectedarray per site. - Keep "Max concurrency" conservative on a large list. This target has no browser fallback, so a site that starts blocking you costs more in wasted requests than a slower, steadier crawl.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (tech-stack-lookup) | $0.00562 per found row (FREE tier, less on paid tiers), $0.00005 actor start, nothing for a miss | One row per site (or per technology) — CMS, ecommerce platform, JS framework, analytics, marketing, chat, CDN, hosting, payments, and security tooling, read live from the page's own HTML and headers | 37 hand-picked signatures, chosen for precision over breadth. A stack that isn't on the list, or that's invisible in HTML/headers (server-side-only tooling), won't show up. |
| BuiltWith | From $295/month | Historic tech data going back years, plus lead lists filtered by technology | If you need "every site that added Shopify last quarter", that's a database question and BuiltWith answers it. This answers "what is this one domain running, right now". |
| Clay | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable from Clay via its HTTP step. |
| Doing it yourself | Your time + maintaining a signature list, handling redirects and blocked responses, and re-testing signatures as sites change their bundlers | The same data | This actor's signature list, the miss-classification (blocked vs. not-found vs. bad-format), and the retry/concurrency handling are the maintenance burden it absorbs. |
Prices for third-party tools are their published list prices as of August 2026 and are not tracked here — check the vendor before relying on the comparison.
FAQ
Why is a row empty, or why does found say false?
Either the input doesn't look like a domain or URL (status: BAD_FORMAT), the site blocked the
request (status: BLOCKED, usually HTTP 403/429/503), the request failed after retries
(status: REQUEST_FAILED), or none of the technologies on the detection list matched
(status: NOT_FOUND). Check the message column for the specific reason. None of these are
billed.
Am I charged for a miss?
No. Actor.charge() only fires when at least one technology is detected on the page. A blocked
site, an unrecognized stack, or a malformed input all produce a row (unless you turn on "Hide rows
with no result") and none of them cost anything.
Is this a live check or a stored database?
Live. Every run fetches the current page and its current response headers — there's no cached
snapshot behind it. That also means a site that changed its stack since the last time you checked
will show the new one, and a site with intermittent blocking may return a different status on
different runs.
Does this handle rate limits or blocking?
Each request goes through Apify's proxy and retries up to twice on failure. If a site consistently
blocks automated requests, it'll come back BLOCKED regardless of retries — lowering "Max
concurrency" can help on sites that are rate-limiting rather than blocking outright, but a site
with strong bot-detection will stay BLOCKED.
Do I need to configure proxies? No. The actor uses Apify's default proxy configuration out of the box; there's nothing to set up unless you want to supply your own proxy group.
Is this GDPR-relevant? No personal data is involved. This reads a website's own public HTML and HTTP response headers — technical metadata about the site's infrastructure, not information about any individual.
Does it see technologies added by JavaScript after the page loads?
Only what's present in the initial HTML response — this doesn't run a headless browser or execute
client-side JavaScript. That covers most of what's on the detection list, since frameworks like
Next.js and Nuxt.js leave a fingerprint (__NEXT_DATA__, __NUXT__) in the HTML the server sends,
and most CMS, analytics, and marketing tags load via a <script src> tag that's also present in
that first response. A technology that's injected entirely by client-side code after the initial
render — with no trace in the server-rendered HTML or headers — won't be picked up.
Can I schedule this to re-check the same list on a cadence? Yes — set up an Apify Task with a schedule (daily, weekly, whatever cadence fits) and point it at this actor with a saved input. Each run is a fresh fetch, so a scheduled task is how you'd catch a site migrating off one platform onto another.
Can an AI agent call this directly? Yes. It's registered on the Apify MCP server — an agent in Claude, Cursor, or another MCP client can find and run it by name ("Tech Stack Lookup | Apify"), or you can call the REST endpoint shown above from any script or workflow tool.
Related actors
- Shopify Store Lookup — once you know a site runs Shopify, pull its storefront details: theme, apps, currency, and more.
- Company Domain Enrichment — broader firmographic and technical enrichment across multiple sources per domain.
- Domain RDAP Lookup — registration data (registrar, creation date, nameservers) for the same domain, from the official RDAP source.