# Shopify Store Products Scraper — Prices, Variants, Stock (`rasareka/shopify-store-products`) Actor

Scrape products from any Shopify store: titles, prices, compare-at (sale) prices, variants, SKUs, stock status, vendors, tags, images and descriptions. Whole catalog or chosen collections. Product or per-variant rows for competitor price monitoring.

- **URL**: https://apify.com/rasareka/shopify-store-products.md
- **Developed by:** [Rasa Reka](https://apify.com/rasareka) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 products

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Shopify Store Products Scraper — Prices, Variants, Stock

**Scrape every product from any Shopify store** in minutes: titles, prices, sale (compare-at) prices, all variants with SKU and stock status, vendors, tags, images and clean descriptions. Paste store URLs, get a ready-to-use product catalog for **competitor price monitoring, product research and dropshipping**.

No login, no API key, no proxies. The Actor reads the public product feed that Shopify stores serve to browsers (`/products.json`), so the data matches what shoppers see on the storefront.

### What you get

- 🏪 Store domain and store name, currency
- 🏷️ Product id, title, handle, product URL, vendor (brand), product type, tags
- 💲 **Lowest price, highest price, compare-at price, on-sale flag** and **in-stock flag**
- 🎨 **All variants** (size, colour...) with id, title, SKU, price, compare-at price, availability, option values and weight in grams
- 🖼️ All image URLs and the main image
- 📝 Description as clean plain text (HTML removed), optional
- 📅 Created, updated and published dates (ISO 8601)
- 📊 Free **STORES** summary per store: product count, vendor count, price range, average price, % on sale, % in stock

### Why this Actor

| | This Actor |
|---|---|
| Input | Store URL, bare domain (`colourpop.com`) or any page of the store. Redirects are followed to the main domain |
| Whole catalog or collections | Both. Add collection handles or collection URLs to scrape only those |
| Output modes | **Product** (one item per product, variants nested) or **Variant** (one flat row per variant, ideal for price tracking in a spreadsheet) |
| Sale detection | `onSale` is true when any variant's compare-at price is above its price |
| Store summary | Free STORES record with price range, vendor count, % on sale |
| Stores that don't work | Listed in a free `FAILED` record with the reason, never charged |
| Login / cookies / proxies | Not needed |

### Use cases

- **Competitor price monitoring:** schedule a daily run in variant mode and track price, compare-at price and stock per SKU over time
- **Product research:** see a competitor's full range, which product types and vendors they carry, what is on sale and what is sold out
- **Dropshipping and sourcing:** find products, prices and images from niche Shopify stores and import them into your own research sheet
- **Market and brand analysis:** compare price ranges and discount depth (% on sale) across many stores in one run
- **Catalog feeds for AI and apps:** structured product data with plain-text descriptions, ready for RAG, search or recommendation pipelines

### Input example

```json
{
  "storeUrls": ["https://www.allbirds.com", "kyliecosmetics.com"],
  "collectionHandles": [],
  "maxProductsPerStore": 1000,
  "outputMode": "product",
  "includeDescription": true
}
```

| Field | What it does |
|---|---|
| `storeUrls` | Store URLs or domains. `https://`, `www.` and page paths are optional |
| `collectionHandles` | Optional. Only scrape these collections (handle such as `mens-shoes` or the full collection URL). Empty = whole catalog |
| `maxProductsPerStore` | Cap per store (default 1000) |
| `outputMode` | `product` (default) or `variant` |
| `includeDescription` | Add `descriptionText` (default true) |

### Output example (product mode, real data)

```json
{
  "store": "www.allbirds.com",
  "storeName": "Allbirds",
  "productId": 7366895894608,
  "title": "Women's Wool Runner - Natural Grey (Light Grey Sole)",
  "handle": "womens-wool-runners-natural-grey",
  "url": "https://www.allbirds.com/products/womens-wool-runners-natural-grey",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": [
    "allbirds::gender => womens",
    "allbirds::material => wool",
    "bfcm-womens",
    "FINAL SALE"
  ],
  "createdAt": "2026-05-26T18:10:21.000Z",
  "updatedAt": "2026-11-07T01:47:43.000Z",
  "publishedAt": "2026-09-25T23:24:03.000Z",
  "price": 49,
  "maxPrice": 49,
  "compareAtPrice": 110,
  "onSale": true,
  "available": true,
  "currency": "USD",
  "variantsCount": 7,
  "variants": [
    {
      "id": 42424464900176,
      "title": "5",
      "sku": "WR3WNCW050",
      "price": 49,
      "compareAtPrice": 110,
      "available": true,
      "option1": "5",
      "option2": null,
      "option3": null,
      "grams": 698
    },
    {
      "id": 42424464932944,
      "title": "6",
      "sku": "WR3WNCW060",
      "price": 49,
      "compareAtPrice": 110,
      "available": false,
      "option1": "6",
      "option2": null,
      "option3": null,
      "grams": 714
    }
  ],
  "options": [
    {
      "name": "Size",
      "values": [
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11"
      ]
    }
  ],
  "images": [
    "https://cdn.shopify.com/s/files/1/1104/4168/files/WR3WNCW_SHOE_LEFT_GLOBAL_WOMENS_WOOL_RUNNER_GREY_LIGHT_GREY_bc7cc3a6-43a0-4dde-8f9e-48a8d80499df.png?v=1779819025",
    "https://cdn.shopify.com/s/files/1/1104/4168/files/WR3WNCW_SHOE_BACK_GLOBAL_WOMENS_WOOL_RUNNER_GREY_LIGHT_GREY_0e004c82-37f9-43cb-aea0-2adb950bf655.png?v=1779819025"
  ],
  "mainImage": "https://cdn.shopify.com/s/files/1/1104/4168/files/WR3WNCW_SHOE_LEFT_GLOBAL_WOMENS_WOOL_RUNNER_GREY_LIGHT_GREY_bc7cc3a6-43a0-4dde-8f9e-48a8d80499df.png?v=1779819025",
  "descriptionText": "The Allbirds Wool Runner is the original wool sneaker that started it all. Called the world's most comfortable shoe, it's flexible, supportive, astonishingly soft, and machine washable. The Wool Runner is breathable, lightweight, and can be worn sockless. Best used for walking, cooler weather, and everyday casual wear.",
  "scrapedAt": "2026-11-07T01:47:42.128Z"
}
```

Shortened for the README: this product really has 7 variants, 4 images and 22 tags. `options` lists the option names and values; `variants` holds every size/colour with its own price, SKU and stock flag.

### Output example (variant mode, real data)

```json
{
  "store": "www.allbirds.com",
  "storeName": "Allbirds",
  "productId": 7292464824400,
  "variantId": 41990816137296,
  "title": "Women's Wool Cruiser - Port (Port Sole)",
  "variantTitle": "5",
  "handle": "womens-wool-cruiser-port-port-sole",
  "url": "https://www.allbirds.com/products/womens-wool-cruiser-port-port-sole?variant=41990816137296",
  "vendor": "Allbirds",
  "productType": null,
  "tags": [
    "11/3/26 DROP",
    "allbirds::cfId => color-f67ac276729fd74d9edd3ad836f79a7e",
    "allbirds::hue => red"
  ],
  "sku": "A12858W050",
  "price": 120,
  "compareAtPrice": null,
  "onSale": false,
  "available": false,
  "currency": "USD",
  "option1": "5",
  "option2": null,
  "option3": null,
  "grams": 1000,
  "image": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12858W090-PORT_SIDE_TRANSPARENT.png?v=1793836076",
  "createdAt": "2026-02-05T01:37:18.000Z",
  "updatedAt": "2026-11-07T01:48:15.000Z",
  "publishedAt": "2026-11-04T23:48:27.000Z",
  "scrapedAt": "2026-11-07T01:48:13.591Z"
}
```

(Tags shortened.) `url` links straight to that variant. Fields a store does not fill are `null`.

Export to JSON, CSV, Excel, or connect to Google Sheets, Make, Zapier, n8n or your own code through the Apify API.

### Free STORES and FAILED records

Open the run's **Storage → Key-value store**:

- **STORES:** one entry per store with the store name, currency, products and variants saved, vendor count and top vendors, min / max / average price, % of products on sale and % in stock.
- **FAILED:** stores (or collections) that could not be scraped, with the reason, for example `store does not expose public products.json`. These are never charged.

Example STORES entry (real data, 300 products scraped from allbirds.com):

```json
{
  "input": "allbirds.com",
  "store": "www.allbirds.com",
  "storeName": "Allbirds",
  "currency": "USD",
  "country": "US",
  "publishedProductsCount": 368,
  "publishedCollectionsCount": 1349,
  "collections": null,
  "productCount": 300,
  "variantCount": 3658,
  "itemsSaved": 300,
  "vendorCount": 2,
  "topVendors": [
    {
      "vendor": "Allbirds",
      "products": 299
    },
    {
      "vendor": "re:do",
      "products": 1
    }
  ],
  "minPrice": 0.8,
  "maxPrice": 165,
  "avgPrice": 111.53,
  "onSalePercent": 3.7,
  "availablePercent": 99.3,
  "pagesFetched": 2,
  "scrapedAt": "2026-11-07T01:47:42.128Z"
}
```

### Pricing

You pay only for results saved to your dataset: one result per product in product mode, or one per variant row in variant mode. Failed stores, the STORES summary and the FAILED record are free. You can set a maximum cost per run in the run options, and the Actor stops cleanly when it is reached.

### Limits (please read)

- **Only Shopify stores that expose the public `/products.json` feed** are supported. Most Shopify stores do, but some turn it off or block it (HTTP 404 or 403). Those stores appear in `FAILED` with the reason `store does not expose public products.json` and cost nothing. Non-Shopify sites are not supported. If only the `www.` or only the bare domain serves the feed, the Actor tries both automatically.
- **No inventory quantities.** Shopify's public feed only says whether a variant is available (in stock or purchasable), not how many units are left.
- Prices are in the store's default currency. Market-specific or customer-specific prices, discount codes and B2B prices are not shown.
- Products hidden from the online store, password-protected stores and draft products are not included.
- Reviews, ratings and metafields are not part of the public feed.
- Headless stores (custom frontend on top of Shopify) work when their Shopify domain answers. Product URLs then point to that domain, which usually redirects to the live site.
- `price` is the lowest variant price and may be 0 for free gifts or samples that some stores list as products.
- The Actor waits about 0.5 s between pages to stay polite, so very large stores (10,000+ products) take a few minutes each. Shopify can rate-limit heavy use; the Actor waits and retries automatically.

### FAQ

**Do I need a Shopify account or API key?** No. The Actor uses the same public product feed that the storefront uses.

**How do I know if a store will work?** Open `https://<store>/products.json` in your browser. If you see product data, the Actor can scrape it.

**Can I track price changes daily?** Yes. Create a schedule in Apify with `outputMode: "variant"` and compare runs by `variantId`, `price`, `compareAtPrice` and `available`.

**Is scraping Shopify stores legal?** This Actor reads only publicly published product information and collects no personal data. You are responsible for how you use the data, including each store's terms and copyright on images and descriptions, and privacy laws such as GDPR.

**Something broken or a field missing?** Open an issue on the Issues tab. We usually reply within 24 hours.

# Actor input Schema

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

Store URLs or bare domains, e.g. https://www.allbirds.com, colourpop.com or mystore.myshopify.com. Any page URL of the store works; the Actor follows redirects to the store's main domain.

## `collectionHandles` (type: `array`):

Optional. Collection handles (the part after /collections/ in the URL, e.g. "mens-shoes") or full collection URLs. Leave empty to scrape the whole catalog. Applied to every store in the run.

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

Stop after this many products for each store. In variant mode, all variants of these products are saved.

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

"product" saves one item per product with its variants nested inside. "variant" saves one flat row per variant (size/colour) with its own price, SKU and stock status. Each saved row is one result.

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

Add the product description as clean plain text (HTML removed) in descriptionText.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com"
  ],
  "collectionHandles": [],
  "maxProductsPerStore": 1000,
  "outputMode": "product",
  "includeDescription": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `stores` (type: `string`):

No description

## `failed` (type: `string`):

No description

# 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": [
        "https://www.allbirds.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rasareka/shopify-store-products").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": ["https://www.allbirds.com"] }

# Run the Actor and wait for it to finish
run = client.actor("rasareka/shopify-store-products").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": [
    "https://www.allbirds.com"
  ]
}' |
apify call rasareka/shopify-store-products --silent --output-dataset

```

## MCP server setup

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

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/eNJm7nP6sGCPYSyBD/builds/JpgQbUBCP4i17mTmY/openapi.json
