# Shopify Scraper — Products, Prices, Variants & Stock (`innolabs/shopify-scraper`) Actor

Shopify scraper API: all products from any Shopify store with variants, prices, compare-at prices, stock, images, tags and collections. Monitor competitors. JSON/CSV/Excel.

- **URL**: https://apify.com/innolabs/shopify-scraper.md
- **Developed by:** [Tristan Leroux](https://apify.com/innolabs) (community)
- **Categories:** E-commerce, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper

Scrape **all products from any Shopify store**: titles, prices, compare-at (was) prices,
discounts, stock flags, every variant with SKU and options, images, tags, vendor, product
type, descriptions and dates. Paste a store homepage, a collection page or a product page
and get clean rows within seconds (thousands of products per store in one run). Optional barcodes
(GTIN/UPC/EAN), inventory quantities, videos and subscription plans. No login, no API key,
no store access needed. Export as JSON, CSV or Excel, or pull the data through the API.

### What does Shopify Scraper do?

Shopify Scraper reads the public product catalog that every Shopify storefront serves and
turns it into one dataset with the same fields for every store:

- **Whole stores** — paste a homepage such as `https://www.allbirds.com/` and get every
  published product, 250 per request: 100 products take a few seconds, a 3,900-product store
  about a minute.
- **Collections** — paste a collection page (`/collections/sale`) or list collection handles
  to scrape only those categories of each store.
- **Single products** — paste product pages to track specific items; they always include
  barcodes, stock quantities (where the store shows them), image alt texts, videos and
  subscription plans.
- **Variants** — sizes, colours and other options with their own SKU, price, compare-at
  price, availability, weight and variant link, nested in the product or one row per
  variant.
- **Prices and discounts** — lowest and highest price, compare-at prices, an `onSale` flag
  and the biggest discount in percent, plus the store's currency.
- **Store details** — store name, currency and the `*.myshopify.com` address behind a custom
  domain.

Custom domains and `*.myshopify.com` addresses both work. Sites that are not Shopify stores
or have a storefront password are reported per URL and do not stop the run.

### Use cases

- **E-commerce brands** — monitor competitors' prices, discounts and new launches every day
  and get alerted when a rival starts a sale.
- **Dropshippers and resellers** — export a supplier's complete catalog with variants, SKUs,
  images and descriptions, ready for import or product research.
- **Market researchers and analysts** — compare catalogs, price points, vendors and product
  types across dozens of Shopify stores in one dataset.
- **Retail and pricing teams** — track stock availability per size and colour to see what
  sells out and when items return.
- **Agencies and lead generators** — size up prospects' stores: number of products, price
  range, currency, vendors and how often the catalog changes.
- **Data and AI teams** — feed product titles, descriptions, tags and images into
  classification, matching or LLM pipelines.

### How much does it cost?

You pay only for the products you get (pay-per-event). Duplicates and items over your
`maxItems` limit are never charged.

| Event | What you pay for | Free plan | Paid plans (from) |
| --- | --- | --- | --- |
| `product` | One product row (or one variant row with "One row per variant") | $1.00 per 1,000 | $0.50 per 1,000 |

"Paid plans (from)" is the Gold-and-above price; Bronze ($0.80) and Silver ($0.60) plans sit
in between. Apify also charges a tiny actor-start fee ($0.00005 per run).

Worked examples (Free plan prices):

1. **Daily competitor price check** — 5 competitor stores with about 400 products each:
   2,000 products × $1.00 / 1,000 = **$2.00 per run** ($1.00 on Gold and above).
2. **One supplier catalog, variant by variant** — 300 products with about 8 sizes/colours
   each, exported with "One row per variant": 2,400 rows × $1.00 / 1,000 = **$2.40**.

The Apify Free plan includes $5 monthly credit, enough for about 5,000 products every month.

### How to use Shopify Scraper

In Apify Console:

1. Open Shopify Scraper and click **Try for free**.
2. Paste store homepages, collection pages or product pages into **Start URLs** (the form is
   prefilled with two real stores).
3. Set **Max products per store** (0 = the whole catalog), and if you like turn on **One row
   per variant**, **Only products in stock** or **Barcodes, stock quantities, videos**.
4. Click **Start**. Most stores finish within seconds to a minute.
5. Open the **Output** tab and export the dataset as JSON, CSV, Excel or HTML.

Through the API:

1. Get your API token in Apify Console → Settings → Integrations.
2. Call the actor with your input JSON (see [Integrations and API](#integrations-and-api)).
3. Read the dataset items from the run.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | array | – | Store homepages (`https://store.com/`), collection pages (`https://store.com/collections/sale`) or product pages (`https://store.com/products/handle`). Custom domains, `*.myshopify.com` and bare domains (`store.com`) work. |
| `maxProductsPerStore` | integer | `0` | Products per store or collection URL; `0` = all. |
| `variantRows` | boolean | `false` | One row per variant instead of one row per product with nested variants. |
| `onlyAvailable` | boolean | `false` | Skip sold-out products (and sold-out variants in variant rows). |
| `collections` | array | – | Collection handles (`mens`, `sale`) or collection URLs: store homepages are scraped only through these collections. |
| `fetchProductDetails` | boolean | `false` | Add barcodes, inventory quantity and policy, image alt texts, videos and subscription plans (one extra request per product, slower). |
| `maxItems` | integer | `1000` | Hard cap on rows for the whole run. |
| `proxyConfiguration` | object | Apify Proxy | Proxy settings. |
| `debug` | boolean | `false` | Log every request. |

Example — two whole stores, only items in stock, one row per variant:

```json
{
    "startUrls": ["https://www.allbirds.com/", "https://colourpop.com/"],
    "maxProductsPerStore": 0,
    "onlyAvailable": true,
    "variantRows": true,
    "maxItems": 20000
}
```

Example — the men's collection of one store plus two tracked products, with barcodes and
stock quantities:

```json
{
    "startUrls": [
        "https://www.allbirds.com/collections/mens",
        "https://www.allbirds.com/products/womens-wool-runners-natural-grey",
        "https://colourpop.com/products/bare-necessity"
    ],
    "fetchProductDetails": true
}
```

### Output

One item per product (variants nested). Real example from 2026-11-03, shortened to two
variants, two images and four tags:

```json
{
    "storeDomain": "www.allbirds.com",
    "storeName": "Allbirds",
    "myshopifyDomain": "weareallbirds.myshopify.com",
    "currency": "USD",
    "productId": 7366895894608,
    "handle": "womens-wool-runners-natural-grey",
    "title": "Women's Wool Runner - Natural Grey (Light Grey Sole)",
    "url": "https://www.allbirds.com/products/womens-wool-runners-natural-grey",
    "vendor": "Allbirds",
    "productType": "Shoes",
    "tags": ["allbirds::carbon-score => 7.07", "allbirds::cfId => color-womens-wool-runners", "allbirds::complete => true", "allbirds::edition => classic"],
    "available": true,
    "priceMin": 49.0,
    "priceMax": 49.0,
    "compareAtPriceMin": 110.0,
    "compareAtPriceMax": 110.0,
    "onSale": true,
    "maxDiscountPercent": 55.5,
    "variantsCount": 7,
    "options": [{ "name": "Size", "values": ["5", "6", "7", "8", "9", "10", "11"] }],
    "variants": [
        {
            "id": 42424464900176,
            "title": "5",
            "sku": "WR3WNCW050",
            "options": { "Size": "5" },
            "price": 49.0,
            "compareAtPrice": 110.0,
            "available": true,
            "grams": 698,
            "barcode": "843416126588",
            "inventoryQuantity": 1,
            "inventoryPolicy": "deny",
            "requiresShipping": true,
            "taxable": true,
            "position": 1,
            "imageUrl": null,
            "url": "https://www.allbirds.com/products/womens-wool-runners-natural-grey?variant=42424464900176",
            "createdAt": "2026-05-26T18:10:22Z",
            "updatedAt": "2026-11-03T17:13:01Z"
        },
        {
            "id": 42424464932944,
            "title": "6",
            "sku": "WR3WNCW060",
            "options": { "Size": "6" },
            "price": 49.0,
            "compareAtPrice": 110.0,
            "available": false,
            "grams": 714,
            "barcode": "843416126595",
            "inventoryQuantity": 0,
            "inventoryPolicy": "deny",
            "requiresShipping": true,
            "taxable": true,
            "position": 2,
            "imageUrl": null,
            "url": "https://www.allbirds.com/products/womens-wool-runners-natural-grey?variant=42424464932944",
            "createdAt": "2026-05-26T18:10:22Z",
            "updatedAt": "2026-11-03T17:13:01Z"
        }
    ],
    "imageUrl": "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",
    "images": [
        {
            "src": "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",
            "alt": null,
            "width": 1600,
            "height": 1600
        }
    ],
    "videos": [],
    "sellingPlans": [],
    "description": "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. ...",
    "descriptionHtml": "<p>The Allbirds Wool Runner is the original wool sneaker that started it all. ...</p>",
    "createdAt": "2026-05-26T18:10:21Z",
    "updatedAt": "2026-11-03T17:13:01Z",
    "publishedAt": "2026-09-25T23:24:03Z",
    "collection": null,
    "collectionTitle": null,
    "sourceUrl": "https://www.allbirds.com/products/womens-wool-runners-natural-grey",
    "scrapedAt": "2026-11-03T17:13:01Z"
}
```

With **One row per variant** each row has the product fields above (without `variants`)
plus `variantId`, `variantTitle`, `sku`, `variantOptions`, `price`, `compareAtPrice`,
`available` (of that variant), `grams`, `barcode`, `inventoryQuantity`, `inventoryPolicy`,
`variantImageUrl`, `variantUrl` and `variantPosition`.

Field notes:

- Prices are numbers in the store's default `currency` (as on the store's main domain).
- `compareAtPrice` is the crossed-out "was" price; `onSale` is true when at least one variant
  costs less than its compare-at price, `maxDiscountPercent` is the biggest such discount.
- `available` on a product is true when at least one variant can be bought.
- `barcode`, `inventoryQuantity`, `inventoryPolicy`, `videos` and `sellingPlans` are filled
  for product URLs and with **Barcodes, stock quantities, videos**; otherwise they are `null`.
  Many stores hide inventory counts, then `inventoryQuantity` stays `null`.
  `inventoryPolicy: "continue"` means the store keeps selling when stock reaches zero.
- `collection` / `collectionTitle` are set for products scraped through a collection.
- `description` is the product description as plain text; `descriptionHtml` keeps the HTML.
- Dates are UTC (ISO 8601). `updatedAt` changes whenever the store edits the product
  (including stock changes).

### Integrations and API

Run the actor and get the items back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/innolabs~shopify-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls": ["https://www.allbirds.com/"], "maxProductsPerStore": 100}'
```

Python with [apify-client](https://docs.apify.com/api/client/python):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("innolabs/shopify-scraper").call(
    run_input={
        "startUrls": ["https://www.allbirds.com/", "https://colourpop.com/collections/lip"],
        "onlyAvailable": True,
    }
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["onSale"]:
        print(item["storeDomain"], item["title"], item["priceMin"], item["compareAtPriceMin"])
```

- **Make, Zapier, n8n** — use the Apify app/node to start the actor and send new or
  discounted products to Google Sheets, Airtable, Slack or email.
- **Scheduling** — create a schedule in Apify Console (e.g. every morning) to keep a daily
  price and stock history of the stores you watch.
- **Webhooks** — trigger your pipeline when a run finishes.
- **AI agents (MCP)** — connect Claude, Cursor or other MCP clients through the Apify MCP
  server at https://mcp.apify.com and let your agent look up products of any Shopify store.

### FAQ

#### Is it legal to scrape Shopify stores?

The scraper only reads the product catalog that a store publishes to every visitor; it does
not log in, place orders or access customer data. Product data is generally not personal
data, but check each store's terms and your local laws for how you use it. This actor is not
affiliated with or endorsed by Shopify.

#### How do I know if a website is a Shopify store?

Just add it. If a URL is not a Shopify store (or the store has switched off its public
product feed, which some large brands with custom storefronts do), the run lists it in the
log and in the run's `SUMMARY` record with "not a Shopify store" and continues with the other
URLs. You are not charged for failed URLs.

#### Do I need proxies?

No setup is needed. The default Apify Proxy works. When a store throttles requests, the
scraper switches to a fresh proxy IP or slows down, and retries automatically.

#### How many products can I get?

A whole store, up to 25,000 products per store or collection URL (a Shopify limit). For
bigger catalogs add collection URLs or use the **Collections** field. `maxProductsPerStore`
and `maxItems` keep the cost under your control.

#### Why are some products missing compared to the website?

The scraper returns products that are published to the online store. Products hidden from
the storefront, gift cards that are not listed, or items only sold in other channels are not
included. Collections only contain the products the store assigned to them.

#### Can I track price and stock changes over time?

Yes. Schedule the actor daily and compare runs by `productId` and variant `id`: `price`,
`compareAtPrice`, `available` and `updatedAt` show what changed. With **Only products in
stock** you get just what can be bought today.

#### What about password-protected stores?

Stores behind a storefront password (often stores that are not launched yet) cannot be read;
they are reported as "password-protected" and skipped.

### Limits

- Up to 25,000 products per store or collection listing (Shopify's own limit); use
  collections to go beyond.
- Prices are in the store's default currency. Local prices of international storefronts
  (other currencies per country) are not included.
- Stores that switched off the public product feed (some large brands with fully custom
  storefronts) cannot be scraped and are reported as not Shopify.
- Inventory quantities are only available where the store exposes them, and only with
  **Barcodes, stock quantities, videos** or for product URLs. That option makes one extra
  request per product and stores throttle these requests, so it runs at about 5–10 products
  per second instead of 60+ (200 products took 30 seconds in our tests).
- Metafields, product reviews and SEO meta tags are not part of the public catalog and are not
  included.

# Changelog

This Actor's version history is a separate document: https://apify.com/innolabs/shopify-scraper/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Shopify store homepages (all products), collection pages (https://store.com/collections/sale) or product pages (https://store.com/products/handle). Custom domains and \*.myshopify.com addresses both work; a bare domain like allbirds.com is fine too.

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

How many products to take from each store or collection URL. 0 = the whole catalog (Shopify serves up to 25,000 products per store or collection listing).

## `variantRows` (type: `boolean`):

Off: one row per product with all variants (sizes, colours) nested in a 'variants' list. On: one flat row per variant with its own SKU, price, compare-at price and stock flag, which is easier in spreadsheets. Each row is one result.

## `onlyAvailable` (type: `boolean`):

Skip products with no variant available for purchase. With 'One row per variant' sold-out variants are skipped too.

## `collections` (type: `array`):

Collection handles (e.g. 'mens' or 'sale') or collection URLs. When set, store homepages in Start URLs are scraped only through these collections. Collection and product URLs are not affected.

## `fetchProductDetails` (type: `boolean`):

Request every product's page data as well: adds variant barcodes (GTIN/UPC/EAN), inventory quantities and policy where the store exposes them, image alt texts, product videos and subscription plans. One extra request per product, so runs take longer. Product URLs always include these details.

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

Hard cap on dataset rows for the whole run (products, or variants with 'One row per variant'). The run stops cleanly when reached.

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

Apify Proxy settings. The default datacenter proxy works for Shopify stores; the scraper backs off automatically when a store throttles.

## `debug` (type: `boolean`):

Log every request with its status code.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.allbirds.com/",
    "https://colourpop.com/"
  ],
  "maxProductsPerStore": 50,
  "variantRows": false,
  "onlyAvailable": false,
  "fetchProductDetails": false,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debug": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

All items produced by the run, one JSON object per record.

# 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 = {
    "startUrls": [
        "https://www.allbirds.com/",
        "https://colourpop.com/"
    ],
    "maxProductsPerStore": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("innolabs/shopify-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 = {
    "startUrls": [
        "https://www.allbirds.com/",
        "https://colourpop.com/",
    ],
    "maxProductsPerStore": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("innolabs/shopify-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 '{
  "startUrls": [
    "https://www.allbirds.com/",
    "https://colourpop.com/"
  ],
  "maxProductsPerStore": 50
}' |
apify call innolabs/shopify-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,innolabs/shopify-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/hhFVPh9QxcELYfu27/builds/exGHCZ1oYQRCSHEVT/openapi.json
