Shopify Store Analyzer — Theme, Apps, Products per Domain
Pricing
$5.00 / 1,000 per store analyzeds
Shopify Store Analyzer — Theme, Apps, Products per Domain
Feed it domains, get one intelligence row per store: is it Shopify (4 detection tiers), theme + version + custom flag, exact product & collection counts, price range, currency, 70+ detectable apps/pixels/payment methods, socials and contact email. No discovery — it analyzes the list you have.
Pricing
$5.00 / 1,000 per store analyzeds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
Shopify Store Analyzer — Theme, Apps & Product Counts per Domain
Feed this Actor the store domains you already hold; it returns one intelligence row per STORE (not per product): isShopify with a 4-tier detection confidence, myshopifyDomain, shopId, themeName, themeVersion, themeIsCustom, productCount, collectionCount, priceMin/priceMax, currency, countryCode, the apps, ad pixels and payment methods detectable from the homepage (70 signatures: 49 apps, 14 pixels, 7 payment methods), socialLinks, and contactEmail where the homepage publishes one. No login, no API key, no browser. No discovery and no crawling beyond the domains you give it — this is the analysis side of the pipe, for lists you already have.
Measured on a real 45-domain mixed list (2026-08-15): 39 billed verdicts — 36 Shopify, 3 correctly identified as not Shopify — and 6 no-verdict domains (4 store-side bot walls, 2 dead) returned as rows but NOT billed. You pay $0.005 only when you get an answer.
{"domains": ["allbirds.com", "gymshark.com", "colourpop.com", "deathwishcoffee.com","stevemadden.com", "kyliecosmetics.com", "fashionnova.com", "ruggable.com"],"maxProductPages": 8,"fetchCollections": true}
Click Try for free and hit Start — that is the input the Actor ships with. A platform run on those shipped defaults returned 8 rows in 35 seconds for $0.04: 8/8 Shopify verdicts including one headless frontend with an open catalog (fashionnova) and one fully headless build (ruggable), each labeled with its detection tier instead of being silently guessed at — and the run log printed its own billing reconciliation: 8 events charged for 8 rows, 1:1.
The wedge: one row per store, and you pick the stores
Nearly every "Shopify scraper" on the Store is a product scraper — you give it one store, it gives you thousands of product rows. This Actor answers a different question: "what is this store?" — asked across a whole list at once. Agencies qualifying a pitch list, app vendors scoring leads by tech stack, and competitive researchers need the store-level row (platform, theme, catalog size, price band, app stack), not 2,000 product rows per domain.
The intelligence comes from three sources no estimate can fake:
- The Shopify globals every Liquid storefront inlines —
Shopify.shop(the myshopify handle),Shopify.theme(name, version, and whether it came from the official theme store), currency, country, locale, shop id. - The public catalog API (
/products.json, paged at 250/page) — an exact product count on most stores (28 of the 33 counted stores were exact at the default depth), plus real price range, vendors, product types, and newest/oldest product publish dates — a live activity signal. - Homepage script signatures — 70 curated app/pixel/payment fingerprints (Klaviyo, Recharge, Rebuy, Gorgias, Attentive, Judge.me, Meta/TikTok/Pinterest pixels, Shop Pay, Afterpay, Klarna…). Measured: 4.5 detected apps per Shopify store on average (max 10).
How this differs from our Shopify App Store Leads Scraper: that Actor walks the Shopify App Store and produces one row per app publisher (support email, developer website, ratings) — it is how you find the sellers of apps. This Actor takes store domains you already hold and analyzes the stores themselves, including which of those apps each store runs. Use them at opposite ends of the same pipeline.
Read this before you buy rows
Five things that would otherwise turn into a refund request.
contactEmailis sparse: 38.9% on the 36-store sample. It is whatever the homepage itself publishes (mailto links first, visible footer text second) — no guessing, no enrichment, no contact-form scraping. If you need an email on every row, pair this with an email finder; do not buy this expecting one.- A verdict is billed even when the answer is "not Shopify." The verdict is the
product when you qualify a mixed list — nytimes.com, stripe.com and zara.com each cost
$0.005 to rule out in the measured run. If you only want Shopify rows kept, set
emitNonShopify: falseand non-Shopify domains are skipped unbilled. - Headless stores return the verdict but few fields. ~19% of the measured Shopify
stores (7 of 36) run custom frontends:
products-json-onlystores (fashionnova) still give you catalog counts and prices but no theme;cdn-assets-onlystores (ruggable) give you little beyond the verdict.shopifyDetectiontells you exactly how sure the row is — filter on it. themeIsCustom: truedoes not mean "not Dawn." It means the theme was not installed from the official theme store. Big stores fork a base theme: deathwishcoffee readsthemeName: "Dawn"+themeIsCustom: true(a customized Dawn). Only 3 of 26 themed stores in the sample ran an unmodified official-store theme (Split, Pipeline, Symmetry — each with itsthemeStoreId).currency/countryCodeare as served to a US datacenter exit IP. Localized storefronts answer per-visitor (the same store served CAD to a Canadian IP and USD through the Apify proxy in testing).priceMin/priceMaxcome from/products.json, which serves the store's base currency regardless of visitor location.
What you get
One row per unique input domain. scrapedAt is a full UTC timestamp; publish dates are
the store's own ISO timestamps.
| Group | Fields | Example |
|---|---|---|
| Verdict | isShopify, shopifyDetection, status, passwordProtected | true · storefront |
| Identity | domain, finalUrl, myshopifyDomain, shopId, storeName, storeDescription | deathwishcoffee.myshopify.com · 2717209 |
| Theme | themeName, themeVersion, themeStoreId, themeIsCustom, themeLabel | Dawn · 10.0.0 · custom |
| Catalog | productCount (+IsExact), collectionCount (+IsExact), vendorCount, topVendors[], topProductTypes[] | 146 exact · 124 collections |
| Pricing | priceMin, priceMax, currency | 5 – 119.99 USD |
| Activity | newestProductPublishedAt, oldestProductPublishedAt | 2026-08-14… |
| Locale | countryCode, locale | US · en |
| Tech stack | appsDetected[], pixelsDetected[], paymentsDetected[], appCount | Klaviyo, Rebuy, Gorgias… |
| Contact | contactEmail, contactEmails[], socialLinks{}, organizationAddress | help@allbirds.com |
| Provenance | error (no-verdict rows only), scrapedAt |
shopifyDetection tiers, strongest first: storefront (Shopify globals on the page) →
products-json-only (headless frontend, open catalog API) → headers-only (Shopify
response headers) → cdn-assets-only (only cdn.shopify.com asset references — treat as
"almost certainly Shopify, headless") → none (not Shopify).
Field fill — measured on 36 Shopify stores
A 45-domain mixed list (big DTC brands, mid-size stores, 3 non-Shopify controls, 1 dead domain), 2026-08-15, through the Apify datacenter proxy. Percentages are against the 36 Shopify-verdict rows, sorted descending so the sparse fields are impossible to miss.
| Field | Fill | Notes |
|---|---|---|
storeName / storeDescription | 97.2% | og:site_name / meta description |
socialLinks | 94.4% | share-links excluded; first profile link per network |
productCount | 91.7% | 28 of 33 exact at the default depth; the rest flagged productCountIsExact: false |
collectionCount | 91.7% | exact up to 1,000 |
priceMin / priceMax | 91.7% | store base currency, from real variant prices |
vendorCount / topVendors / topProductTypes | 91.7% | |
newestProductPublishedAt / oldest… | 91.7% | catalog activity signal |
appsDetected (≥1) | 91.7% | average 4.5 apps per store, max 10 |
paymentsDetected (≥1) | 91.7% | Shop Pay, Afterpay, Klarna, Sezzle, Affirm, PayPal, Apple Pay |
myshopifyDomain / shopId | 77.8% | absent on headless frontends |
currency / countryCode / locale | 77.8% | absent on headless frontends |
pixelsDetected (≥1) | 75.0% | Meta, TikTok, Pinterest, Snap, GTM/GA4, Microsoft, Criteo… |
themeName / themeVersion | 72.2% | headless stores have no Liquid theme |
contactEmail | 38.9% | homepage-published only — no guessing. Read gotcha 1. |
organizationAddress | 16.7% | JSON-LD only; most stores publish none |
themeStoreId | low on big brands — 0 of 8 on the shipped default | Not a gap: it is null at the source whenever a store runs a forked or bespoke theme, which every large DTC brand does. Re-probed live 2026-08-19: allbirds, deathwishcoffee, stevemadden, kyliecosmetics, colourpop, brooklinen, thesill, pinklily, trueclassictees, ohpolly all publish "theme_store_id":null; beardbrand.com (stock Split theme) publishes 842 and the Actor returns it. themeStoreId: null alongside a non-null themeName is exactly what themeIsCustom: true means. |
Every run prints its own measured fill in the log. The shipped 8-domain default printed: productCount 87.5%, apps 87.5%, socials 87.5%, theme 75.0%, email 25.0%.
How to run it
1. Qualify a list (the usual job)
{ "domains": ["brand-a.com", "brand-b.com", "brand-c.com"] }
Paste up to 10,000 domains, with or without https:// — duplicate spellings
(www.brand-a.com, https://brand-a.com/about) are normalized and dropped before
billing (measured: a 6-entry input with 3 duplicate spellings billed exactly 2 rows).
2. Shopify-only output
{ "domains": ["…"], "emitNonShopify": false }
Non-Shopify domains are skipped and never billed. Use when your list is scraped from ads or marketplaces and you only want the Shopify stores kept.
3. Fast verdicts, no catalog walk
{ "domains": ["…"], "maxProductPages": 1, "fetchCollections": false }
2 requests per store instead of 3–5. You keep the verdict, theme, apps, socials and email;
productCount stays exact only for stores under 250 products. Same price per store —
depth costs you time, not money.
4. Deep catalog counts
{ "domains": ["…"], "maxProductPages": 40 }
Counts up to 10,000 products exactly. productCountIsExact: false on a row means the cap
stopped the walk and the true catalog is larger than the number shown.
Scheduling and integrations
Save the input as a Task and attach an Apify Schedule for a weekly re-scan of your target accounts (theme changes, app installs/uninstalls and catalog growth are all re-measured each run). The dataset exports to CSV/JSON/Excel/Sheets and the standard Apify integrations (Zapier, Make, n8n, webhooks, MCP). From the API:
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~shopify-store-analyzer/runs?token=YOUR_TOKEN" \-H 'Content-Type: application/json' \-d '{"domains":["allbirds.com","gymshark.com"]}'
Sample row
A real row from an actual run on the shipped defaults, 2026-08-15:
{"domain": "deathwishcoffee.com","finalUrl": "https://www.deathwishcoffee.com/","status": "analyzed","isShopify": true,"shopifyDetection": "storefront","passwordProtected": false,"myshopifyDomain": "deathwishcoffee.myshopify.com","shopId": 2717209,"storeName": "Death Wish Coffee","storeDescription": "Bold, Smooth and Delicious since 2012. We source USDA Organic and Fair Trade Certified arabica and robusta coffee beans…","themeName": "Dawn","themeVersion": "10.0.0","themeStoreId": null,"themeIsCustom": true,"themeLabel": "Copy of deathwish-shopify-20/production","productCount": 146,"productCountIsExact": true,"collectionCount": 124,"collectionCountIsExact": true,"priceMin": 5,"priceMax": 119.99,"currency": "USD","countryCode": "US","locale": "en","vendorCount": 15,"topVendors": ["Death Wish Coffee Company", "Death Wish Coffee", "Sportiqe", "Planet Apparel", "Deneen Pottery"],"topProductTypes": ["Apparel", "Coffee", "Accessories", "Coffee Bundle", "On-The-Go Drinkware"],"newestProductPublishedAt": "2026-08-14T08:40:37-04:00","oldestProductPublishedAt": "2014-03-12T13:35:00-04:00","appsDetected": ["Klaviyo", "Rebuy", "Gorgias", "Postscript", "Smile.io"],"pixelsDetected": ["Google Tag Manager"],"paymentsDetected": ["Shop Pay", "Afterpay", "Apple Pay"],"appCount": 5,"socialLinks": {"twitter": "https://twitter.com/deathwishcoffee","facebook": "https://www.facebook.com/deathwishcoffee","instagram": "https://www.instagram.com/deathwishcoffee/","youtube": "https://www.youtube.com/channel/UC1uurJMded2rRJioe8yka7g"},"contactEmail": null,"contactEmails": [],"organizationAddress": null,"error": null,"scrapedAt": "2026-08-15T04:10:06.861Z"}
Fields people misread:
themeLabelis the store's own internal theme name () — often more revealing than"Copy of deathwish-shopify-20/production"themeNameitself.priceMin: 5is a real $5 variant (stickers, samples). Judge the price band with both ends, not just the floor.contactEmail: nullon a store with 5 detected apps is normal — this brand publishes no email on its homepage. That is the 38.9% fill from the table above, not a bug.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
domains | string list | 8 live stores | The stores to analyze. With or without https://; duplicates dropped before billing; up to 10,000 per run. Send no domains at all (a bare {} from the API or an agent) and the Actor does not fail — it runs a 3-store demo sample (allbirds.com, deathwishcoffee.com, colourpop.com) so you get real rows to inspect. Those 3 rows are billed like any other verdict: $0.015. |
maxProductPages | integer | 8 | Catalog pages of 250 to walk. 8 = exact counts up to 2,000 products. 0 skips the catalog. |
fetchCollections | boolean | true | Count collections (exact up to 1,000). |
emitNonShopify | boolean | true | Off = non-Shopify domains skipped, never billed. |
maxConcurrency | integer | 5 | Parallel domains. Requests within one store are always sequential — no store ever sees concurrent traffic from this Actor. |
proxyConfiguration | proxy | Apify datacenter | Measured: Shopify serves identical bytes on every rung, so the cheap rung is the default. Blocked statuses retry once through RESIDENTIAL automatically. |
Pricing
$0.005 per store analyzed — $5 per 1,000. Charged on the store-analyzed event.
No monthly platform fee from this Actor.
| Run | Stores | Cost |
|---|---|---|
| The shipped default | 8 | $0.04 |
| An agency pitch list | 200 | $1.00 |
| A lead-scoring batch | 1,000 | $5.00 |
| A full TAM sweep | 10,000 | $50.00 |
- You are charged per verdict, not per attempt. Blocked and unreachable domains are
pushed as rows (so you can see what happened, with the HTTP status in
error) without the charge event — in the measured 45-domain run, 6 of 45 domains were not billed. - Duplicate spellings are dropped before billing.
- Rows are charged as they are pushed (
Actor.pushData(items, 'store-analyzed')), so a budget cap gets you whole rows and stops, never a half-billed dataset. - Depth settings (
maxProductPages,fetchCollections) change run time, not price. - A run that hits its time limit stops cleanly instead of being killed. The Actor keeps a
wall-clock budget from the platform's own run timeout, stops taking new domains before it,
delivers everything already collected, and ends Succeeded with a status message naming how
many domains were never started. Those un-started domains are not billed — re-run them
with a longer run timeout or in smaller batches. Measured 2026-08-19: 301 domains at
maxProductPages: 40against a 120 s wall → finished in 74 s, 3/3 runs, 57 verdict rows delivered and billed, 0 rows lost, never TIMED-OUT.
How it works, and what was measured
Per domain, 3–5 requests at the defaults: the homepage (Shopify globals, JSON-LD, app/pixel
signatures, socials, mailto links), then /products.json?limit=250 paged until a short
page (an exact count) or the cap, then /collections.json. The catalog probe runs even
when the homepage carries no Shopify markers — that is how headless-frontend stores with an
open catalog API (fashionnova) are caught instead of misfiled as "not Shopify."
Transport ladder, measured 2026-08-15 with got-scraping:
| Rung | Result |
|---|---|
| Direct (reported and then ignored — home-IP recon lies) | 200, identical bytes |
| Apify proxy, datacenter | 200, identical bytes |
| Apify RESIDENTIAL | 200, identical bytes |
| Apify RESIDENTIAL + pinned session | 200, identical bytes |
Shopify's edge does not discriminate between rungs, so the cheap datacenter rung is the
default and you are not billed residential bandwidth you do not need. Sustained load
through the Apify datacenter proxy: 20 calls (10 domains × homepage + catalog) at
concurrency 4 = 18/20 = 90.0% in 26.5 s — and both misses were bombas.com, which serves
HTTP 429 on every rung including residential and a home IP (a store-side bot wall).
Excluding that one walled store: 18/18 = 100%. Store-side walls exist on roughly 1 in
10 big-brand domains (4 of 45 measured: bombas, meundies, huckberry, mizzenandmain) — those
rows come back unbilled with the status in error.
Duplicates, measured in both directions: contiguous /products.json pages returned
0 duplicate product ids across two full catalog walks (291 and 146 products), so counts
never double-count; and the run itself asserts one row per unique domain before it is
allowed to succeed.
robots.txt: Shopify's generated robots.txt opens with "Public product, collection,
page, blog, policy, cart, and localized HTML is crawlable" (fetched live 2026-08-15). Its
Disallow list (/admin, /cart/, /checkout, /orders, /account, /cart.js, sort/
filter parameter URLs…) never mentions /products.json or /collections.json — and this
Actor touches none of the disallowed paths. It reads currency from the homepage globals
specifically because /cart.js is disallowed. Per-store traffic is sequential — one
request at a time per domain, concurrency only across different domains.
Honest limits
- No revenue or traffic estimates. Rival analyzers headline "revenue estimates" —
numbers no public endpoint provides, so they are models, not measurements. Everything in
this row is read from the store itself; nothing is modeled. If a field cannot be read, it
is
null, not guessed. contactEmailfill is 38.9% and comes from the homepage only. This is not an email finder.- App detection is homepage-signature-based. Apps that load only on product/checkout
pages (some upsell and shipping apps), server-side apps, and apps with no front-end
footprint are invisible.
appsDetectedis a floor, not a census — 70 signatures curated for the highest-signal revenue apps (reviews, subscriptions, SMS/email, support, BNPL). - Headless stores yield thin rows (see gotcha 3) — the verdict and detection tier are the deliverable there.
- ~1 in 10 big-brand domains runs its own bot wall and comes back
blocked, unbilled. This Actor does not fight DataDome/Kasada-class walls for a $0.005 row. - Password-protected (pre-launch) stores return
passwordProtected: truewith the verdict — useful as a "store about to launch" signal, but catalog fields are null. - One row per domain, ever. Multi-region stores on separate domains (gymshark.com vs gymshark.eu? paste both) are separate rows; this Actor never expands your list.
Who buys this
- Agencies pitching Shopify services — qualify a pitch list: platform, theme (stock vs custom), catalog size, price band and app gaps, in one CSV.
- Shopify app vendors — score leads by tech stack: who runs a competitor's app, who
runs no reviews/subscription/SMS app at all (
appsDetectedas a gap map). - BNPL / payments / logistics vendors —
paymentsDetectedshows exactly which of Shop Pay / Afterpay / Klarna / Sezzle / Affirm a store already offers. - Competitive & market researchers — catalog size, price range, publish-date activity and ad-pixel mix across a whole vertical.
- Theme and CRO agencies —
themeName/themeVersion/themeIsCustomfind stores on aging stock themes.
FAQ
Does this find Shopify stores for me? No — you supply the domains; it analyzes them. Store discovery is a different job (our Shopify App Store Leads Scraper covers the app-publisher side).
Do I need a login, API key or residential proxies? No. Everything is public storefront data, and the measured ladder shows the cheap datacenter rung works everywhere Shopify itself answers.
Will every row have an email? No — 38.9% measured. It is whatever the homepage publishes. Read gotcha 1 before buying.
Is the product count exact?
productCountIsExact tells you per row. At the default depth, 28 of 33 counted stores
were exact; raise maxProductPages to 40 for exact counts up to 10,000.
What happens on a non-Shopify domain?
A billed isShopify: false verdict row (that answer is usually the point), or set
emitNonShopify: false to skip them unbilled.
What happens on a blocked or dead domain?
An unbilled row with status: "blocked" or "unreachable" and the HTTP status in
error. You never pay for a non-answer.
Will a run ever succeed with zero rows? No. A run that produces zero billable verdicts throws, and the error message breaks down exactly what happened (blocked / unreachable / filtered) so you can tell a list problem from a site problem.
Legal & fair use
This Actor reads public storefront pages and the public catalog endpoints each store serves to every visitor. It does not log in, does not touch checkout, cart, admin or any robots.txt-disallowed path, and collects nothing behind authentication. Store rows can contain business contact data. You are responsible for complying with each store's terms, Shopify's terms, and with how you use the data. Shopify® is a trademark of Shopify Inc.; this Actor is not affiliated with, endorsed by, or connected to Shopify Inc. or any store analyzed.
Feedback
Found a store that misdetects, or want a signature added to the app list? Open an issue on the Issues tab — signature additions ship fast. If the Actor earns it, a review on the Reviews tab helps other buyers find it.