← Guides

How-to

How to run Shopify Store Audit & Tech-Stack Lead Scorer

Step-by-step: the exact input, what each field does, and the real rows that come back from public storefront.

You have a list of Shopify domains and a question about each one: what is this store actually running? Which reviews app, which email tool, which theme, how big is the catalog, are they discounting. If you sell a Shopify app, that question decides who is worth an email. If you run an agency, it decides what you pitch. The manual answer is to open each store, hit View Source, and read the script tags — which works, and is genuinely fine for three stores. It stops being fine at thirty, and it produces nothing you can sort, filter, or hand to a CRM.

This actor does that read for you across a whole list and returns it as rows.

What you’ll need

Nothing but an Apify account. The actor talks to public storefront directly.

No API key, no merchant permission, no app install, no CAPTCHA-solving credentials. Everything it reads is what a visitor with the browser devtools open can already see: the storefront HTML and the store’s own /products.json. That’s why it’s cheap and why it rarely breaks. The proxy toggle exists but is off by default — turn it on only if a specific store blocks the run.

Step 1 — Open the actor

Open Shopify Store Audit & Tech-Stack Lead Scorer on the Apify Store and press Try for free. The actor’s own page on this site lists its live health and pricing.

The Shopify Store Audit & Tech-Stack Lead Scorer listing on the Apify Store
The listing on the Apify Store. Pricing and the nightly health record are both public.

Step 2 — Fill in the input

The input form for Shopify Store Audit & Tech-Stack Lead Scorer in the Apify Console
The input form, as it appears in the Apify Console.
FieldTypeRequiredWhat it does
domainsarrayyesShopify store domains or URLs to audit, e.g. ‘allbirds.com’. Paste a list to bulk-audit a prospect list. We audit the domains you provide (we don’t discover stores).
targetGapsarraynoApp categories that should be MISSING for a store to count as a lead — sets ‘matches_criteria’. E.g. a reviews-app vendor sets [‘reviews’]. Categories: reviews, email, search,…
includeProductsbooleannoWhen off (default), return store signals only (fast, cheap — the lead-gen need). Turn on to also include the (slimmed) product catalog.
compareToPreviousbooleannoWhen on (default), diff each store against the previous run and report what changed — apps added/removed and product-count delta. Run on a schedule to track stores over time. (default True)
maxProductsPerStoreintegernoCap on products sampled per store (bounds cost). Product count caps here for very large stores. (default 1000)

Two fields decide what you actually get. domains is the one people misread: this actor audits the domains you give it. It does not go looking for Shopify stores, and there is no “find me all stores in cosmetics” mode — you bring the list, it enriches the list. Bare hosts or full URLs both work; https://Allbirds.com/collections/mens and allbirds.com normalise to the same thing.

targetGaps is what turns an audit into a lead score. Give it the app categories that must be absent for a store to count as a prospect, and matches_criteria comes back true only when every one of them is missing. A reviews-app vendor sets ["reviews"]. Leave targetGaps empty and matches_criteria is null — you still get all the detection, just no verdict. The seven categories are reviews, email, search, upsell, loyalty, subscription and chat.

A working input:

{
  "domains": [
    "allbirds.com",
    "colourpop.com"
  ],
  "targetGaps": [
    "reviews"
  ],
  "compareToPrevious": true
}

Step 3 — Run it

Press Start. Rows are pushed to the dataset as they’re found, so the run log fills in as it works.

You get exactly one row per domain you submitted — no more, no fewer. Non-Shopify domains and domains that don’t respond still come back as rows, with is_shopify: false, so you can see what was dropped rather than silently losing it. An empty detected_apps list is not a failure: it usually means the store genuinely runs none of the apps in the signature list, or loads them server-side where the storefront markup doesn’t give them away. Check is_shopify first — if that’s true and the app list is empty, the detection ran and found nothing.

Step 4 — Read the output

Each run returns a labelled table, not raw JSON:

The output fields returned by Shopify Store Audit & Tech-Stack Lead Scorer
The output view — every field the actor returns, named and typed.

Every row looks like this — a real row from a real run:

{
  "domain": "allbirds.com",
  "is_shopify": true,
  "detection_signals": [
    "cdn.shopify.com",
    "Shopify.theme"
  ],
  "shopify_plus": false,
  "theme_name": "Dawn",
  "detected_apps": [
    {
      "name": "Yotpo",
      "category": "reviews"
    },
    {
      "name": "Klaviyo",
      "category": "email"
    }
  ],
  "app_categories": [
    "email",
    "reviews"
  ],
  "has_reviews_app": true,
  "has_email_app": true,
  "has_search_app": false,
  "missing_categories": [
    "search",
    "upsell",
    "loyalty",
    "subscription",
    "chat"
  ],
  "matches_criteria": false,
  "product_count": 629,
  "price_min": 18.0,
  "price_max": 160.0,
  "currency": "USD",
  "discount_rate": 0.12,
  "new_products_30d": 7,
  "products_json_accessible": true,
  "first_seen": false,
  "apps_added": [
    "Klaviyo"
  ],
  "apps_removed": [],
  "product_count_delta": 12,
  "source_url": "https://allbirds.com"
}
FieldExample valueMeaning
domainallbirds.comStore domain audited
is_shopifyTrueWhether the domain is a confirmed Shopify store
detected_apps[{"name": "Yotpo", "category": "reviews"}, {"name": "Klaviyo", "category": "email"}]Installed apps detected from the storefront ({name, category})
app_categories["email", "reviews"]Categories of detected apps
missing_categories["search", "upsell", "loyalty", "subscription", "chat"]App categories with NO detected app — the prospecting gaps
matches_criteriaFalseTrue when all your target-gap categories are missing (a lead)
has_reviews_app / has_search_app / …Per-category presence flags (reviews, email, search, upsell, loyalty, subscription, chat)
theme_nameDawnStorefront theme name

Most people came for missing_categories and matches_criteria — that’s the prospecting verdict. A few things to watch. matches_criteria is null, not false, when you didn’t set targetGaps. product_count is capped by maxProductsPerStore, so a store with more products than the cap reports the cap and sets product_count_is_floor: true — treat that number as a floor, not a total. And when a store has disabled /products.json (products_json_accessible: false), the count falls back to a sitemap tally and the catalog signals that depend on product data — price_min, price_max, discount_rate, new_products_30d — come back null. That’s expected, not a bug.

Step 5 — Export it

Open the Dataset tab and export to CSV, JSON, or Excel — or pull the same rows from the API, which is what you want if this is going to run on a schedule.

What it costs

Pay-per-store: $0.01 per Shopify store audited, so a 1,000-store prospect list is about $10.00. Domains that turn out not to be Shopify, or that never respond, are pushed to the dataset for free — you don’t pay to find out a domain was a dead end. Apify’s own platform compute is billed separately on your plan; this actor uses plain HTTP requests rather than a browser, which keeps that side small.

Where the data comes from

This reads public storefront directly. The target is Public storefront JSON. Every night a canary runs this actor against that live source and diffs the result against a frozen fixture — what “verified” means.

There is no privileged access here and no integration. Every Shopify store serves a /products.json endpoint with its catalog in it, and serves storefront HTML with its theme name and its apps’ script tags embedded — that is the whole source. The annoyance isn’t getting the data, it’s that it arrives as an undifferentiated wall of JSON and markup: apps are identified by CDN hostnames buried in script tags, the product count is only the length of a paginated list, and roughly one store in seven turns /products.json off entirely and needs a sitemap fallback. Doing that across a list by hand is the part nobody wants to do twice.

See it used

Detect a Shopify Store’s Apps & Theme is the single-store version — one domain, what it runs. Find Shopify Stores Missing a Reviews App is the app-vendor version — a list in, the ones with a gap flagged on the way out. And Audit a Competitor’s Shopify Tech Stack uses the same row to read a rival’s stack and catalog posture.

If what you actually want is to watch a store change rather than describe it once, reach for the Shopify Price & Stock Change Tracker instead: this actor tells you what a store is, the tracker tells you what moved — price changes, stock flips, products added and pulled. The natural pairing is to audit once and then watch.

Last updated 2026-07-13