# Shopify Store Scraper — All Products, Variants, Prices & Stock (`yugenox/shopify-store-products-scraper`) Actor

Scrape any Shopify store's full catalogue: products, variants, SKUs, prices, compare-at prices, discounts, stock status, vendors, tags, images and descriptions. Collections, one row per variant, barcodes and stock counts, and a monitor mode for new products, price changes and sell-outs.

- **URL**: https://apify.com/yugenox/shopify-store-products-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 results

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 Scraper: all products, variants, prices and stock

Scrape the **whole catalogue of any Shopify store**: every product with its title, vendor, product type, tags, all variants (size, colour …) with **SKU, price, compare-at ("was") price, discount and stock status**, images, description and dates. Read a whole store, only some **collections**, or a list of **product URLs**. Get **one row per product** (variants as a list) or **one row per variant**, add **barcodes (UPC / EAN / GTIN) and stock quantities**, and switch on **monitor mode** to get only what changed since the last run: new products, price changes, sales starting or ending, sell-outs, back in stock and removed products. No login, no cookies, no browser, no Shopify app or API key.

### What you get

| | |
|---|---|
| **Product** | Product ID, title, handle, URL, vendor / brand, product type, tags, created / published / updated dates |
| **Price** | Price (cheapest variant on product rows), compare-at price, on sale, discount %, lowest and highest variant price, store currency |
| **Stock** | In stock, number of variants in stock; optional stock quantity per variant (where the store shows it) |
| **Variants** | Variant ID, title, SKU, price, compare-at price, in stock, options (size / colour / …), weight, requires shipping, variant image; optional barcode |
| **Media and text** | Main image, all image URLs, description as plain text and as the store's HTML |
| **Monitor mode** | Change type (new / changed / removed / unchanged), what changed in plain words, previous price, previous compare-at price, previously in stock |

### How to use it

1. Paste one or more stores into **Shopify store URLs**: a domain (`allbirds.com`), a store URL, a collection URL (`https://www.allbirds.com/collections/mens`) or a product URL. Mix as many stores as you like.
2. Set **Max products per store** (0 = the whole catalogue) and run.
3. Optional: **One row per variant**, **Include stock quantity and barcode**, **Only these collections**, **Monitor mode**.

#### Input examples

The first 10 products of a store (the quick test):

```json
{
  "storeUrls": ["https://www.allbirds.com"],
  "maxProductsPerStore": 10
}
```

Whole catalogues of several stores, one row per variant (SKU-level price list):

```json
{
  "storeUrls": ["allbirds.com", "colourpop.com", "kyliecosmetics.com"],
  "maxProductsPerStore": 0,
  "outputPerVariant": true
}
```

Daily competitor price and stock monitor: schedule it once a day and get only the changes:

```json
{
  "storeUrls": ["allbirds.com"],
  "maxProductsPerStore": 0,
  "monitorMode": true
}
```

Only the sale and new-arrivals collections, with barcodes and stock counts:

```json
{
  "storeUrls": ["colourpop.com"],
  "collectionHandles": ["sale", "new"],
  "includeStockDetails": true
}
```

### Output example

One product row (trimmed: the `variants` list holds all 7 sizes, `images` all 5 images):

```json
{
  "storeUrl": "https://www.allbirds.com",
  "storeName": "Allbirds",
  "productId": "7340901859408",
  "title": "Women's Allbirds Flip Flop - Dusty Pink",
  "handle": "womens-allbirds-flip-flop-dusty-pink",
  "url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": ["allbirds::edition => limited", "allbirds::gender => womens", "allbirds::material => sugar"],
  "price": 50,
  "compareAtPrice": null,
  "currency": "USD",
  "onSale": false,
  "discountPercent": null,
  "available": true,
  "priceMin": 50,
  "priceMax": 50,
  "variantsCount": 7,
  "availableVariantsCount": 1,
  "options": [{ "name": "Size", "values": ["5", "6", "7", "8", "9", "10", "11"] }],
  "variants": [
    { "variantId": "42146889039952", "title": "5", "sku": "A12513W050", "barcode": null, "price": 50, "compareAtPrice": null, "available": false, "option1": "5", "option2": null, "option3": null, "grams": 455, "inventoryQuantity": null, "requiresShipping": true, "imageUrl": null, "position": 1 }
  ],
  "imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345",
  "imagesCount": 5,
  "description": "Sun on your feet. Comfort underneath. Light, easy, and made for warm weather, these flip flops bring everyday comfort to …",
  "createdAt": "2026-03-27T14:03:50-07:00",
  "publishedAt": "2026-09-25T16:58:13-07:00",
  "updatedAt": "2026-10-28T21:46:59-07:00",
  "collection": null,
  "source": "catalog",
  "changeType": null,
  "scrapedAt": "2026-10-29T04:46:59.925Z"
}
```

With **Include stock quantity and barcode**, every variant also gets `barcode` (e.g. `"196942293089"`) and `inventoryQuantity` (e.g. `3750`) where the store shows them. In **monitor mode** each saved row also says what changed (an illustration, trimmed):

```json
{
  "title": "Women's Tree Dasher Relay - Blizzard",
  "price": 67,
  "compareAtPrice": 135,
  "discountPercent": 50,
  "available": true,
  "changeType": "changed",
  "changes": ["price 135.00 → 67.00", "compare-at price none → 135.00", "\"8\": sold out"],
  "previousPrice": 135,
  "previousCompareAtPrice": null,
  "previousAvailable": true
}
```

Every row has the same columns; a value the store doesn't publish is `null`. The dataset has three views: **Products**, **Variants** and **Changes**.

### Pricing

Pay per result, no subscription:

- **$0.80 per 1,000 rows** (a product, a variant row, or a change in monitor mode).
- **Stock details**: $0.30 per 1,000 products, only when you turn on *Include stock quantity and barcode*.
- **Product checked** (monitor mode only): $0.40 per 1,000 unchanged products, which are not saved.
- **Actor start**: $0.002 per run.

So the first 10 products of a store cost about $0.01, a 1,000-product catalogue $0.80, and a daily monitor of a 1,000-product store where 20 products changed about $0.41. Stores that refuse the requests, products that can't be read and rows over your limits are never charged. See the Pricing tab.

### Use cases

- **Competitor price monitoring**: a daily monitor-mode run on your competitors' stores tells you every price change, new sale, new product and sell-out.
- **Assortment and catalogue analysis**: product types, vendors, tags, variant counts and price ranges of any DTC brand.
- **Dropshipping and product research**: find best-sellers' prices, discounts and stock, export SKU-level price lists.
- **Shopify app and agency prospecting**: see what a store sells, how big its catalogue is and how it is organised.
- **MAP / price compliance for brands**: check what resellers on Shopify charge for your products, by SKU or barcode.

### Tips

- **Monitor mode remembers the last run per store and per scope** (whole store, a collection, a set of product URLs). Keep the input the same between scheduled runs. The first run saves everything as `new`: that's the baseline.
- **One row per product is cheaper**: all variants are in the `variants` list of the product row. Turn on *One row per variant* when you need a flat SKU list (each variant row counts as one result).
- **Stock quantities**: many stores hide them; `inventoryQuantity` is then `null`. Barcodes are shown by most stores.
- Product URLs (`…/products/<handle>`) always come with barcode and stock quantity, at no extra charge.
- Monitoring several very large stores (10,000+ products each) in one run? Give the run 1 GB of memory.

### FAQ

**Do I need a Shopify account, an API key or the store owner's permission?** No. The scraper reads the same public product information any shopper's browser sees on the store. It never logs in, never adds anything to a cart and never touches checkout.

**Which stores work?** Any store running on Shopify, including stores on their own domain (most big DTC brands). For stores that hide their product list, the scraper reads the products one by one from the store's own sitemap and product pages; that's slower, but you get the same columns (tags and dates may be empty). Password-protected stores and stores that block automated visitors are skipped with a clear message and no charge.

**How fast is it?** A normal store gives 250 products per request (1–2 seconds), so a 5,000-product catalogue takes well under a minute. Three stores are read side by side. Stores that hide their product list are read at a few products per second.

**Why do some rows have `available: null` or no compare-at price?** The store doesn't publish that value. `compareAtPrice` is `null` when there is no "was" price.

**What currency are the prices in?** The store's own currency (`currency` column), as the store lists them for its main market.

**Which proxy should I use?** Keep the default (Apify Proxy, datacenter). It comes with every Apify plan, including the free one. If a store throttles datacenter IPs, the run retries that store's product data on residential proxies when your account has them, at no extra charge to you.

**Is it legal to scrape Shopify stores?** This Actor only collects publicly available data: the product information that every Shopify store shows to any visitor without an account, such as titles, prices, variants, stock status and images. Collecting publicly available data is generally legal, but you're responsible for how you use it, including the stores' terms and any copyright in their images and descriptions. The scraper follows each store's robots.txt and skips stores that refuse automated visitors. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. It reads only public product data. It never logs in and never reads customer accounts, orders, carts, reviews or any information about people.

*This Actor is not affiliated with, endorsed by or sponsored by Shopify or any of the stores mentioned.*

# Actor input Schema

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

Stores to scrape: a domain ("allbirds.com"), a store URL, a collection URL ("https://www.allbirds.com/collections/mens" = only that collection) or a product URL ("…/products/<handle>" = only that product). Mix as many as you like; each store is read once.

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

Optional: collection handles (the part after /collections/ in the URL, e.g. "mens", "sale", "new-arrivals"). When set, only these collections of the stores above are read instead of the whole catalogue. Each row says which collection it came from.

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

Stop reading a store after this many products. 0 = the whole catalogue.

## `maxItems` (type: `integer`):

Maximum number of dataset rows (and charged results) for the whole run, across all stores. 0 = no limit. In monitor mode it limits the saved changes; how many products are compared (and charged as "Product checked") is set by Max products per store.

## `outputPerVariant` (type: `boolean`):

Save one row per variant (size / colour / …) with its own SKU, price, compare-at price, stock status and options, instead of one row per product with the variants as a list. Each variant row counts as one result.

## `includeStockDetails` (type: `boolean`):

Also read each product's own data to add every variant's barcode (UPC / EAN / GTIN) and stock quantity, where the store shows it (many stores hide quantities). One extra request per product, charged as "Stock details" (see the Pricing tab). Product URLs always include them, free.

## `monitorMode` (type: `boolean`):

Compare with the previous run that had the same store and scope, and save only new products, price and compare-at price changes, sell-outs, back-in-stock and removed products (each row has changeType, changes and the previous values). The first run saves everything as "new" (the baseline). Unchanged products are not saved and are charged as "Product checked", half the price of a result. Schedule it daily to track competitors.

## `saveUnchanged` (type: `boolean`):

In monitor mode, save unchanged products too (changeType "unchanged"), charged as normal results. Useful for a full daily snapshot with change flags.

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

Apify Proxy (datacenter) is the default and comes with every Apify plan. If a store keeps throttling datacenter IPs (HTTP 429 / 430), its product data is retried on residential proxies when your account has them, at no extra charge to you.

## `_noResidential` (type: `boolean`):

Internal (canaries): behave exactly like an account without the RESIDENTIAL proxy group.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com"
  ],
  "maxProductsPerStore": 10,
  "maxItems": 0,
  "outputPerVariant": false,
  "includeStockDetails": false,
  "monitorMode": false,
  "saveUnchanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "_noResidential": false
}
```

# Actor output Schema

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

All scraped products (or variants, or changes in monitor mode).

## `run` (type: `string`):

Status and statistics for this run.

## `report` (type: `string`):

RUN_REPORT: how the run ended, per-store results, counts, requests and proxy use.

# 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"
    ],
    "maxProductsPerStore": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/shopify-store-products-scraper").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"],
    "maxProductsPerStore": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/shopify-store-products-scraper").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"
  ],
  "maxProductsPerStore": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/shopify-store-products-scraper --silent --output-dataset

```

## MCP server setup

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

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/YOC2qDUttGeSuXCTN/builds/Z5svmqEcoX2ecyl6K/openapi.json
