I run a visa agency in Bali, and a dull part of that job is deciding who to write to. A clinic running Shopify with no consent banner is a different conversation from a hotel chain on Adobe. So one of the Apify Actors I maintain takes a list of domains and returns one flat row per website — CMS, e-commerce platform, framework, analytics, ad pixels, payments, CDN, consent tool, chat widget — plus the e-mails and social profiles it finds on the same page: Wappalyzer Scraper. No browser, no extension, no API key. $0.01 per website.
The input prefill is three domains, and my own health checks have been hammering that prefill since early October. Which means I now have something better than a demo: 29 runs between 6 October and 1 November 2026 on the same three domains, plus a panel I ran this morning on 20 well-known brands.
The 29-run panel said something I did not want to read.
The same domain, two completely different companies
Across those 29 runs, gymshark.com produced 29 rows and exactly two distinct values in the url field:
https://us.checkout.gymshark.com/ 28 rows https://www.gymshark.com/ 1 row (12 Oct 2026) Here are the two stacks, side by side:
us.checkout.gymshark.com Shopify · Cloudflare · AWS S3 · HTTP/3 · HSTS · Apple Pay · Shop Pay · Shopify Analytics www.gymshark.com Shopify · React · Next.js · Amazon CloudFront · HSTS · OneTrust · Apple Pay Three technologies are shared. Everything else disagrees — including the CDN, which is Cloudflare on one page and CloudFront on the other, and the framework, which exists on one page and not the other. The readVia field says direct in both cases. No error, no retry, no warning.
And the contacts half of the row collapses completely:
rows with at least one social profile gymshark.com 1 of 29 allbirds.com 27 of 27 notion.so 25 of 27 That single social-profile row for Gymshark is the 12 October row — the one that reached the home page, where the footer carries Facebook, Instagram, LinkedIn, X, YouTube and TikTok. A checkout page has no footer. So 28 of 29 times, this Actor truthfully reported that gymshark.com has no social presence, because the page it was handed does not.
Nothing here is a scraping failure. Gymshark's edge decides, per request, that my IP belongs on a checkout host, and HTTP 301 is not an error. The row is a correct description of a page nobody meant me to measure.
Where this goes wrong in practice
The input that sells this Actor looks like this:
{ "domains": ["gymshark.com", "allbirds.com", "brooklinen.com"], "mustUse": ["shopify"], "mustNotUse": ["klaviyo"], "includeContacts": true } Read it out loud: Shopify stores that have not bought Klaviyo yet. Now feed it the checkout row. Shopify: present. Klaviyo: absent — because checkout pages carry almost no marketing tags. Gymshark survives the filter and lands in a prospect list of companies that "don't use Klaviyo", with no e-mail attached. A mustNotUse filter is only as honest as the page it was evaluated against, and I had not written that down anywhere.
The 20-brand panel: who gets redirected
This morning, one run, 20 brands, 9.1 seconds, $0.20. All 20 answered — 16 on a plain request, 4 only through a residential proxy. 128 technologies, median 6 per site, range 2 to 13.
Six of the 20 rows did not describe the bare home page:
gymshark.com -> https://us.checkout.gymshark.com/ (different host) notion.so -> https://www.notion.com/ (different domain) lego.com -> https://www.lego.com/en-us uniqlo.com -> https://www.uniqlo.com/us/en/ samsung.com -> https://www.samsung.com/us/ hm.com -> https://www.hm.com/entrance.ahtml?orguri=%2F The technology counts line up with that list almost perfectly. The fat rows are direct-to-consumer stores that answer on their real home page: brooklinen 13, glossier 12, allbirds 12, decathlon 9. The thin rows are global brands: adidas 2, uniqlo 2, booking 2, zara 3, hm 3.
Adidas does not run two technologies. Four of those rows were header-only — every item in technologiesDetailed had foundIn: "header":
uniqlo.com 2 Akamai, HSTS booking.com 2 Amazon CloudFront, HSTS adidas.com 2 Akamai, HSTS zara.com 3 Akamai, HTTP/3, HSTS That is not a stack. That is "I reached a CDN and the document behind it told me nothing." The useful rule I now apply to my own exports: a row with ≤3 technologies and no foundIn: "page" evidence is a missing measurement, not a minimal stack. Drop it or re-queue it; do not count it in a market share tally. In this panel that is 4 of 20 rows — 20% of the bill, 0% of the signal.
One site answered three identical calls three different ways
I then ran only the four proxy-needing domains, three times, inside ten minutes. Three of them were stable down to the technology:
zara.com 3 techs, identical in all 3 passes adidas.com 2 techs, identical in all 3 passes asos.com 7 techs, identical in all 3 passes (jQuery, Akamai, HSTS, Mixpanel, New Relic, OneTrust, Optimizely) hm.com was not:
pass 1 https://www.hm.com/entrance.ahtml?orguri=%2F Akamai · ASP.NET · HSTS pass 2 https://www2.hm.com/en_us/index.html React · Next.js · Akamai · HSTS · Apple Pay pass 3 https://www2.hm.com/it_it/index.html React · Next.js · Akamai · HSTS · Apple Pay Different host, different locale, and a poweredBy field that flips between ASP.NET and Next.js. Both answers are true: the region gate at entrance.ahtml is an old ASP.NET page, and the localized storefront on www2 is a Next.js app. Which one you get depends on the exit IP of the proxy that happened to answer. A fourth page, www2.hm.com/es_mx/index.html, came back when I passed the www2 host explicitly in the same hour.
So drift is not a property of the proxy. It is a property of the site: anything with a region gate in front of it will hand different machines different pages, and a technographic column built from one pass will quietly disagree with itself next month.
You cannot aim it at a deep page
Worth knowing before you try: this Actor normalises every input down to a host and reads that host's root. I fed it six deep URLs to prove the point:
input actually read https://www.uniqlo.com/us/en/men -> https://www.uniqlo.com/us/en/ https://www.booking.com/index.en-gb.html-> https://booking.com/ https://www.zara.com/us/ -> https://www.zara.com/ https://www.adidas.com/us -> https://www.adidas.com/ https://www.gymshark.com/collections/all-> https://us.checkout.gymshark.com/ Path and www. are stripped, the root is fetched, and redirects take it from there. That is deliberate — when the input is 50,000 domains from a CRM export, the home page is the only URL you know exists — but it means this is the wrong tool for "what does this one landing page run". For that, point a browser-based crawler at the exact URL.
Calling it, and reading the answer honestly
curl -s -X POST \ "https://api.apify.com/v2/acts/lergassy~wappalyzer-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"domains":["allbirds.com","brooklinen.com"],"includeContacts":true}' Three fields decide whether you trust the rest of the row, and I would read them in this order:
-
url— is the host the one you asked for? Compare it to your input before anything else. If it drifted, the technologies belong to another page. -
readVia—directorproxy. Proxy rows are geo-dependent, so for those the locale and sometimes the whole page can change between passes. -
technologiesDetailed[].foundIn—page,headerorhint. Header evidence describes the edge (CDN, server banner, HSTS); page evidence describes the application. Market-share questions need page evidence.
The contacts half has its own honest limit. In the 20-brand panel, 13 sites gave at least one social profile and only 3 gave an e-mail — home pages mostly don't publish one. And Sephora's row contained age@3x.svg among its five addresses: a retina asset filename that satisfies an e-mail regex perfectly. I am tightening that filter, but the general point stands — e-mails harvested from markup need a verification step before anyone sends to them. I use a separate Actor for that rather than pretending a regex is enough.
What this is good for: counting platforms across a market, filtering a big list by platform before a human looks at it, spotting missing pixels or consent banners across a client portfolio, bulk enrichment at a tenth of a cent per field. What it is not good for: a specific page, exact version numbers, or anything injected only after JavaScript runs — there is no browser here, which is also why a thousand domains cost $10 and take minutes.
And the one line I should have put in the README on day one: the row describes the page the redirect gave you, not the company you typed.

