# Shopify Store Scraper - Products, Prices, Apps & Store Leads (`boztek-ltd/shopify-store-intelligence`) Actor

Scrape any Shopify store: full catalog with variants, SKUs and prices, collections, theme, installed apps, plus emails and socials for lead gen. Tracks price drops.

- **URL**: https://apify.com/boztek-ltd/shopify-store-intelligence.md
- **Developed by:** [Boztek LTD](https://apify.com/boztek-ltd) (community)
- **Categories:** E-commerce, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 store analyzeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Shopify Store Scraper — Products, Prices, Apps & Store Leads

Profile any Shopify store from its **public endpoints**. One run gives you the store's identity, its full catalog with variants and prices, its collections, the theme it runs, the apps it has installed, and the emails and social profiles it publishes.

Point it at a domain. If the site is Shopify, you get everything below. If it is not, it is reported and skipped — and never charged.

### What you get

**Per store**

| | |
|---|---|
| Identity | Store name, `.myshopify.com` domain, shop ID, description |
| Location & money | City, province, country, currency, money format, countries shipped to |
| Catalog size | Products pulled, products published, collection count |
| Pricing | Cheapest, most expensive and average product price; how many are on sale; how many are in stock |
| Merchandising | Top vendors, top product types, newest product date, collection list with product counts |
| Tech | Active theme, recognised Shopify apps (reviews, email, SMS, subscriptions, page builders, pixels, BNPL) |
| Contact | Emails, phone numbers, Instagram / Facebook / TikTok / X / YouTube / Pinterest / LinkedIn handles |
| Payments | Accepted card brands, Shop Pay Installments availability |

**Per product**

Title, handle, vendor, product type, tags, product URL, image, price range across variants, highest compare-at price, derived discount percentage, stock status, option names, publish/update dates, and every variant with its SKU, price, compare-at price, weight and availability.

### Track what changes

Turn on **`trackChanges`** and each run is compared against the previous one:

- **New products** — what just launched, with prices
- **Removed products** — what was pulled or sold out of the catalog
- **Price changes** — old price, new price and the percentage move

The first run records a baseline; from the second run on, you get a diff. Schedule it daily and you have competitor price monitoring — or a launch feed for a niche you sell into.

### What does it cost?

Pay-per-event. Non-Shopify domains cost nothing.

| Event | Price | When |
|---|---|---|
| Store analyzed | **$0.01** | Per confirmed Shopify store profiled |
| Product scraped | **$0.0002** | Per product row |
| Contacts & tech extracted | **$0.005** | Per store, only with enrichment on |
| Change report | **$0.02** | Per store, only from the second run on |
| Actor start | **$0.00005** | Once per run |

| Scenario | Cost |
|---|---|
| 100 stores, profiles + contacts, no products (`outputMode: stores`) | ~$1.50 |
| One store, full 5,000-product catalog | ~$1.02 |
| Daily price watch, 10 stores × 2,000 products | ~$4.30/day |

Set `outputMode` to `stores` for lead generation and you never pay the per-product rate.

### Output

Everything lands in one dataset, tagged with `recordType` so you can split it. The Console ships with a **Store profiles** view and a **Products** view. The samples below show the field shape; the numbers are illustrative.

```json
{
  "recordType": "store",
  "domain": "www.allbirds.com",
  "storeName": "Allbirds",
  "myshopifyDomain": "weareallbirds.myshopify.com",
  "city": "Beverly Hills",
  "province": "California",
  "country": "US",
  "currency": "USD",
  "productCount": 291,
  "collectionCount": 1345,
  "priceMin": 60,
  "priceMax": 160,
  "priceAvg": 112,
  "onSaleCount": 34,
  "availableCount": 251,
  "topVendors": ["Allbirds"],
  "newestProductAt": "2026-07-08T18:32:23.000Z",
  "themeName": "allbirds-theme",
  "apps": ["Attentive", "Google Tag Manager", "Yotpo"],
  "emails": ["help@allbirds.com"],
  "socialProfiles": [
    "instagram:allbirds", "facebook:weareallbirds", "tiktok:weareallbirds",
    "twitter:allbirds", "pinterest:weareallbirds"
  ],
  "changes": { "newProducts": 3, "removedProducts": 1, "priceChanges": 12 }
}
```

```json
{
  "recordType": "product",
  "domain": "www.allbirds.com",
  "title": "Men's Cruiser - Shadow Blue (Natural White Sole)",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "priceMin": 105,
  "priceMax": 105,
  "compareAtMax": null,
  "discountPercent": null,
  "currency": "USD",
  "available": true,
  "variantCount": 13,
  "productUrl": "https://www.allbirds.com/products/mens-cruiser-shadow-blue-natural-white-sole",
  "variants": [
    { "variantId": 41, "title": "8", "sku": "A12856M080", "price": 105, "available": true }
  ]
}
```

`compareAtMax` and `discountPercent` are filled in only when the store publishes a compare-at price above the selling price — a stale compare-at that equals or undercuts the price is ignored rather than reported as a fake discount.

### Input options

| Option | Type | Default | Description |
|---|---|---|---|
| `storeUrls` | array | **required** | Domains or URLs. `.myshopify.com` works too |
| `outputMode` | string | `both` | `both` / `stores` (cheapest) / `products` |
| `maxProductsPerStore` | integer | `0` | Cap the catalog per store (`0` = all) |
| `includeVariants` | boolean | `true` | Include every variant with SKU and price |
| `includeDescription` | boolean | `false` | Include product descriptions |
| `descriptionFormat` | string | `markdown` | `markdown` / `text` / `html` |
| `includeCollections` | boolean | `true` | List collections on the store row |
| `extractContacts` | boolean | `true` | Emails, phones, socials, theme and apps |
| `trackChanges` | boolean | `false` | Diff against the previous run |
| `stateStoreName` | string | `shopify-store-intelligence-state` | Key-value store holding the baseline |
| `maxConcurrency` | integer | `5` | Parallel requests across stores |
| `proxyConfiguration` | object | off | Optional — not needed |

### Use cases

1. **Agency lead generation** — feed a list of domains, keep the ones that are Shopify, and get the merchant's email, socials, catalog size and installed apps in one row. Apps tell you what they already pay for.
2. **Competitor price monitoring** — schedule daily with `trackChanges` and get every price move with its percentage.
3. **Product research and dropshipping** — spot what a niche is launching, at what price, and what is selling out.
4. **Market sizing** — catalog size, price bands and vendor mix across a whole segment.
5. **App and theme adoption research** — see which apps and themes a cohort of stores actually runs.

### How stores are detected

The Actor asks each domain for `/meta.json` — one small request that also returns the store's name, country, currency and catalog counts. Stores that disable it fall back to `/products.json`, which every Shopify store serves. Both the apex and the `www.` host are tried, so `gymshark.com` and `www.allbirds.com` both resolve.

### Limitations

- **Contact extraction only sees server-rendered HTML.** Stores whose footer is rendered client-side publish no email or social links in the page source, so those fields come back empty. Gymshark is a real example: its home page has no `mailto:` or social links and it serves no `/pages/contact`. Catalog, pricing, theme and app data are unaffected.
- **`themeStoreId` is empty for custom themes.** Shopify sets `theme_store_id` to `null` for themes that did not come from the Theme Store, so the field is genuinely absent rather than missed.
- **App detection is signature-based.** It recognises around 50 widely used apps from their scripts. An app that leaves no trace in the page source will not be listed, so treat the list as "at least these", not an exhaustive audit.
- **`products.json` returns published products only.** Draft, archived and hidden products are not exposed by Shopify, and neither are inventory quantities — only per-variant availability.
- **Some stores throttle or disable the public feeds.** A store that returns nothing is reported in `RUN_SUMMARY` rather than silently dropped.
- **Prices are as the store serves them** — in the store's own default currency and market. Geo-specific pricing is not requested.

# Actor input Schema

## `storeUrls` (type: `array`):

One entry per store. A bare domain (`allbirds.com`), a full URL (`https://www.allbirds.com`) or a `.myshopify.com` domain all work. Non-Shopify domains are reported and skipped without being charged.

## `outputMode` (type: `string`):

`stores` is the cheap lead-generation view: one row per store, no per-product charge. `products` is the catalog and price view. `both` writes store rows first, then product rows, each tagged with `recordType`.

## `maxProductsPerStore` (type: `integer`):

Cap the catalog pulled from each store. `0` means the whole catalog.

## `includeVariants` (type: `boolean`):

Include every variant with its SKU, price, compare-at price and availability. Turn off for one row per product with a price range only.

## `includeDescription` (type: `boolean`):

Include the product description. Long descriptions make exports much larger.

## `descriptionFormat` (type: `string`):

`markdown` is the most useful for LLM pipelines, `text` is the smallest, `html` preserves the original storefront markup.

## `includeCollections` (type: `boolean`):

List the store's public collections and their product counts on the store row.

## `extractContacts` (type: `boolean`):

Read the store's home and contact pages for emails, phone numbers, social profiles, the active theme and recognisable Shopify apps. Charged once per store.

## `trackChanges` (type: `boolean`):

Compare this run against the previous one and report new products, removed products and price changes. The first run only records a baseline. Best combined with a schedule.

## `stateStoreName` (type: `string`):

Named key-value store holding the price/catalog baseline. Use different names to keep separate schedules independent.

## `maxConcurrency` (type: `integer`):

Parallel requests across stores.

## `proxyConfiguration` (type: `object`):

Optional. Shopify's public JSON endpoints answer fine from Apify's datacenter IPs.

## Actor input object example

```json
{
  "storeUrls": [
    "allbirds.com",
    "gymshark.com"
  ],
  "outputMode": "both",
  "maxProductsPerStore": 0,
  "includeVariants": true,
  "includeDescription": false,
  "descriptionFormat": "markdown",
  "includeCollections": true,
  "extractContacts": true,
  "trackChanges": false,
  "stateStoreName": "shopify-store-intelligence-state",
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `all` (type: `string`):

Store profile rows and product rows together. Each item carries a recordType of either store or product.

## `storeColumns` (type: `string`):

Narrowed to the store-level columns: domain, name, country, currency, catalog size, price range, emails, socials, theme and apps. Note this selects columns, not rows, so product rows still appear with those columns blank.

## `productColumns` (type: `string`):

Narrowed to the product-level columns: title, vendor, type, price range, compare-at price, discount, stock and product URL. Same caveat: this selects columns, not rows.

## `csv` (type: `string`):

The full dataset as CSV, with nested fields flattened into columns.

## `runSummary` (type: `string`):

How many domains were requested, how many were confirmed as Shopify and profiled, which ones were skipped and why, and the count of each event charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "storeUrls": [
        "allbirds.com",
        "gymshark.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("boztek-ltd/shopify-store-intelligence").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "storeUrls": [
        "allbirds.com",
        "gymshark.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("boztek-ltd/shopify-store-intelligence").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "storeUrls": [
    "allbirds.com",
    "gymshark.com"
  ]
}' |
apify call boztek-ltd/shopify-store-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,boztek-ltd/shopify-store-intelligence"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/e8l6ZaPymbKpvovB6/builds/gy696gR9FGqqcJ7aK/openapi.json
