Tech Stack & CMS Detector — Technology Check, Pixels & Shopify
Pricing
from $3.50 / 1,000 tech stack check (site read)s
Tech Stack & CMS Detector — Technology Check, Pixels & Shopify
Tech stack and CMS detection, a Wappalyzer / BuiltWith alternative. Finds pixels other detectors miss: those loaded through Google Tag Manager and Shopify apps. Domains in, one row out: CMS, analytics, chat, payments, ad pixels and marketing tools, with evidence and tracking IDs. JSON/CSV/API/MCP.
Pricing
from $3.50 / 1,000 tech stack check (site read)s
Rating
0.0
(0)
Developer
Brenton Keller
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
2
Monthly active users
8 days ago
Last modified
Categories
Share
Tech Stack & CMS Detector: Technology Check, Pixels & Shopify
Give it a list of domains. You get one row per site listing the CMS, analytics, chat, payment, ad-pixel and marketing tools it runs. Each technology comes with the evidence that matched it, and the tracking IDs it found (GA4, Google Ads, Meta pixel, TikTok, Pinterest, GTM, HubSpot portal, Klaviyo account).
An alternative to BuiltWith and Wappalyzer that finds pixels other detectors miss: those loaded through Google Tag Manager and Shopify apps. On 20 Shopify stores tested, that was 48 technologies a page scan does not see.
Built for lead enrichment. Use it to find Shopify stores running Klaviyo, B2B sites with a LinkedIn Insight Tag but no chat widget, or WordPress sites with no analytics at all.
Why this one sees more
It reads the Google Tag Manager container. Most ad pixels (Meta, LinkedIn, TikTok, Bing, Reddit) are not in a site's HTML. They are loaded by Tag Manager after the page opens. A detector that only scans the HTML reports these sites as having no pixels. This actor fetches the site's public gtm.js container, the same file every visitor's browser loads, and counts the tags it actually fires.
On 30 well-known sites tested, half of everything found (120 of 239 technologies) was visible only inside the Tag Manager container.
It reads the container carefully. GTM's own runtime code mentions Google's ad products on every site, and paused tags still ship inside the container. Counting either would report technologies the site does not use. Only live tags, the templates they call, and the variables they read are counted.
It reads Shopify's web pixels. Shopify apps such as Facebook & Instagram, Google & YouTube, TikTok, Pinterest and Klaviyo now install their tracking as sandboxed "web pixels". These never appear as script tags or in Tag Manager. The storefront still embeds each app pixel's settings, and the actor reads them. Only pixels that are actually configured count: an app installed but never set up is not reported. On 20 Shopify stores tested, this found 48 ad pixels and tools that a page scan misses, including the Meta pixel on 8 stores.
It doesn't sell you a guess. If Cloudflare, DataDome, Akamai, Fastly, PerimeterX, Imperva, AWS WAF or Vercel answers with a challenge page instead of the site, you get an error row saying which one, and you are not charged. A tool that reads the challenge page instead would report "uses Cloudflare, nothing else".
Every detection shows its evidence. For example, meta_pixel: "tag manager GTM-ABC123: connect.facebook.net/en_US/fbevents.js". If you doubt a row, you can see why it says what it says.
Input
{"domains": ["allbirds.com", "webflow.com", "techcrunch.com", "https://acme.com/pricing"]}
- A bare domain is tried at
https://domain/, thenhttps://www.domain/, thenhttp://domain/. - A full URL is fetched exactly as given. Use this to check a pricing or checkout page, which is where payment providers usually load.
| Option | Default | What it does |
|---|---|---|
inspectTagManager | true | Reads Tag Manager containers. Costs one extra request per container. Containers shared across sites are fetched once. |
maxTagManagerContainers | 2 | Most sites have one container. Some load an agency container and an in-house one. |
includeEvidence | true | Up to three evidence strings per technology. Turn it off for smaller rows; detected_via stays. |
maxConcurrency | 8 | Sites checked in parallel. |
requestTimeoutSecs | 20 | A site that doesn't answer in time becomes a free error row. |
proxyConfiguration | none | Not needed for most sites. Add a residential proxy for sites behind aggressive bot protection. |
Output
One row per input, in input order. A line that repeats an earlier one (ignoring case and a trailing slash) is checked and charged once, so it gets no second row. shop.acme.com and acme.com are different sites and each get a row.
{"query": "allbirds.com","final_domain": "allbirds.com","tech_count": 15,"cms": ["shopify"],"analytics": ["elevar", "google_tag_manager", "microsoft_clarity"],"chat": [],"payments": [],"ads": ["criteo", "google_ads", "meta_pixel", "microsoft_ads", "outbrain", "reddit_pixel", "tiktok_pixel"],"marketing": ["attentive", "yotpo"],"consent": ["onetrust"],"frameworks": [],"hosting": ["cloudflare"],"tracking_ids": {"google_tag_manager": ["GTM-TH8KRSBJ"], "google_ads": ["AW-786649265", "AW-736740096"], "reddit_pixel": ["t2_b4kw4fzh"], "microsoft_clarity": ["yrlg0i3snv"]},"shopify_store": "weareallbirds.myshopify.com","is_parked": false,"detected_via": {"meta_pixel": ["tag_manager"], "reddit_pixel": ["shopify_pixel"], "shopify": ["page", "headers"], "...": []},"evidence": {"meta_pixel": ["tag manager GTM-TH8KRSBJ: connect.facebook.net/en_US/fbevents.js"], "...": []},"checked_at": "2026-10-12T19:40:07Z"}
Categories:
cms: CMS or storefront platformanalyticschat: chat and support widgetspaymentsads: ad pixelsmarketing: marketing automation, email, ABM, reviews and schedulersconsent: cookie consent toolsframeworkshosting: hosting and CDN
The fingerprint table covers 180+ technologies, including:
- CMS: WordPress, WooCommerce, Shopify, Webflow, Squarespace, Wix, Framer, HubSpot CMS, Drupal, BigCommerce, Magento, Salesforce Commerce Cloud, Contentful and Sanity.
- Analytics: Google Analytics, Segment, Mixpanel, PostHog, Amplitude, Heap, Hotjar and FullStory.
- Chat: Intercom, Zendesk, Drift, Crisp, LiveChat, HubSpot Chat, Gorgias and Qualified.
- Payments: Stripe, PayPal, Braintree, Shop Pay, Klarna, Afterpay, Affirm, Adyen and Paddle.
- Ad pixels: Google Ads, Meta, TikTok, LinkedIn, X, Pinterest, Snap, Microsoft, Reddit and AppLovin Axon.
- Marketing: HubSpot, Marketo, Pardot, Mailchimp, Klaviyo, Braze, Attentive, Nosto, Dynamic Yield, 6sense, ZoomInfo, Clearbit and Demandbase.
tracking_ids lists every ID found on the site (page, Tag Manager container, Shopify pixels). Each ID's technology is always in the lists too: an ID configured in the site's settings but not loaded on the homepage still counts, with evidence id configured: …. The exception is a Google tag (GT-…), which can send to Analytics, Ads or both, so it names no single technology.
detected_via says where each technology was seen. On allbirds.com, 8 of its 15 technologies come only from tag_manager or shopify_pixel, meaning a page scan would not have found them.
| Value | Seen in |
|---|---|
page | the homepage HTML: script and link URLs, inline scripts, markup, meta tags |
headers | HTTP response headers and cookies |
tag_manager | a live tag in the site's Google Tag Manager container |
shopify_pixel | a configured Shopify app web pixel |
tracking_id | only a configured tracking ID (see above), no loader on the homepage |
implied | implied by another technology (WooCommerce means WordPress) |
checked_at is when the site was read, in UTC (ISO 8601). Every row has it, error rows included.
shopify_store is the store's permanent *.myshopify.com handle. It stays the same when the store changes its domain.
What it cannot see
This actor reads the HTML a browser receives. It does not run the page's JavaScript. So these are invisible to it:
- Tools bundled into the site's own JavaScript. stripe.com serves Stripe.js from its own bundle, and posthog.com does the same with PostHog. The vendor's script URL never appears.
- Enterprise sites with custom infrastructure show fewer third-party technologies. Large companies often serve everything from their own domains and CDNs: dell.com, atlassian.com and stripe.com each returned 1 technology in our tests, against a median of 8 across 30 well-known sites. A short list there is a correct result, meaning "nothing third-party is loaded on the homepage", not "this company uses no tools".
- Tools loaded only on other pages. Payment providers usually load only at checkout or on the pricing page. An empty
paymentslist on a homepage is not proof the site takes no payments. Pass that page's URL to check it. - Shopify custom pixels. App pixels are read (see above), but Shopify doesn't expose the code of a store's own custom pixels. The
hintsays how many a store runs. - Tags that only fire after login, and server-side tagging where the browser never contacts the vendor.
Use it from the API, Clay, MCP or a schedule
API
One call runs the actor and returns the rows:
curl -X POST "https://api.apify.com/v2/acts/brenton8907~tech-stack-check/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"domains": ["allbirds.com", "webflow.com"]}'
This waits up to 5 minutes, which is enough for about 100 domains. For longer lists, start the run with POST /v2/acts/brenton8907~tech-stack-check/runs and read its dataset when it finishes. Add maxTotalChargeUsd to the query string to cap what a call can spend.
Clay
Clay's native Apify integration is included in all Clay plans. You connect your own Apify account:
- In your table, Add enrichment, search for "Apify" and choose Run Apify Actor.
- Connect your Apify account with your Apify API token, found on the Integrations page of Apify Console.
- Choose the actor
brenton8907/tech-stack-check. - Under Input Data, enter
{"domains": [<Domain>]}, inserting your domain column where<Domain>is. Clay's rule: put the key in quotes, but not the column token. - Run it on one row first, then map the fields you need into columns:
cms,analytics,ads,marketing,tracking_ids,shopify_storeanderror.
A row with error set was not charged. For hundreds of domains, one Console run with the whole list is faster: export CSV and join it back on query, which echoes your input exactly.
Alternative: Clay's HTTP API enrichment (Growth plan or higher). Use this when you want the response shape under your control:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.apify.com/v2/acts/brenton8907~tech-stack-check/run-sync-get-dataset-items |
| Query params | token = your Apify API token (store it as a Clay secret) · maxTotalChargeUsd = 0.01 · optionally fields = query,tech_count,cms,analytics,chat,payments,ads,marketing,tracking_ids,shopify_store,hint,error,checked_at |
| Header | Content-Type: application/json |
| Body | {"domains": ["{{domain}}"]}, with {{domain}} replaced by your domain column |
The response is a one-element array, so map fields from item 0. For example, filter on 0.ads containing meta_pixel, or on 0.cms containing shopify.
MCP (Claude, Cursor and other AI agents)
{"mcpServers": {"tech-stack-check": {"type": "http","url": "https://mcp.apify.com/?tools=brenton8907/tech-stack-check","headers": { "Authorization": "Bearer ${APIFY_TOKEN}" }}}}
This exposes the tool brenton8907--tech-stack-check, which takes the input documented above. Each site it reads costs the normal $0.005.
Scheduled runs: watch stacks change
In Console, save your domain list as a task and add it to a schedule (for example weekly, cron 0 6 * * 1). Each run writes a new dataset with a checked_at on every row. To see which sites added or dropped a technology, compare the last two runs:
import os, requestsAPI, TOKEN, TASK = 'https://api.apify.com/v2', os.environ['APIFY_TOKEN'], '<your-task-id>'runs = requests.get(f'{API}/actor-tasks/{TASK}/runs', params={'token': TOKEN, 'status': 'SUCCEEDED', 'desc': 1, 'limit': 2}).json()['data']['items']def rows(run):items = requests.get(f'{API}/datasets/{run["defaultDatasetId"]}/items',params={'token': TOKEN, 'clean': 1}).json()return {r['query']: r for r in items if not r.get('error')}new, old = rows(runs[0]), rows(runs[1])for q in sorted(new.keys() & old.keys()):added = set(new[q]['technologies']) - set(old[q]['technologies'])dropped = set(old[q]['technologies']) - set(new[q]['technologies'])if added or dropped:print(q, old[q]['checked_at'], '->', new[q]['checked_at'], '+', sorted(added), '-', sorted(dropped))
Error rows are skipped, so a site that was unreachable this week does not look like it dropped everything. Some sites rotate their tags between visits, so treat a single disappearance as a reason to recheck, not as proof the site stopped using a tool. Apify also connects scheduled runs to Zapier, Make and n8n.
Pricing
$0.005 per site read. Error rows are free. These include unreachable sites, bot-protection challenges, invalid inputs and public suffixes. One site's price covers the homepage fetch and its Tag Manager containers.
Errors
| Error | Meaning |
|---|---|
Blocked by <vendor> bot protection | A challenge page answered instead of the site. Not charged. Try a residential proxy. |
Site unreachable: … | DNS, TLS, timeout or an HTTP error on every URL variant. Not charged. |
… is a public suffix | You entered something like co.uk, which can't be a website. Not charged. |
A hint field explains caveats on successful rows: a redirect to another domain, a parked domain, an unreadable Tag Manager container, Shopify custom pixels that can't be read, or a page truncated at 3 MB.
Wappalyzer and BuiltWith are trademarks of their owners. This actor is an independent tool, is not affiliated with or endorsed by either, and uses its own fingerprint set rather than their data.