# Shopify Products Scraper (`deriverge/shopify-products-scraper`) Actor

Products of any Shopify store, collection or single product link, with every variant's price, compare-at price, availability and SKU. Stock counts and barcodes are added where the store publishes them, and scheduled runs flag new products, price drops and sell-outs.

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

## Pricing

from $0.25 / 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.
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 Products Scraper

Shopify Products Scraper exports products from Shopify stores through the public JSON feed of their storefront, one row per product with all its variants inside: title, vendor, type, tags, price range, compare-at price and discount, availability per size or color, SKUs, weights, images and dates. It accepts a store domain, a collection link or a product link. Barcodes and unit stock counts come on request, the counts only where the store publishes them. A watch name turns scheduled runs into price and stock alerts. It costs $0.50 per 1,000 products on the Free plan ($0.25 on Business), and the $5 of monthly credit in Apify's Free plan covers about 10,000 products.

### Output fields

| Field | Description |
|---|---|
| `store`, `storeName` | The store's main domain (`allbirds.com` comes back as `www.allbirds.com`) and the store's name. |
| `productId`, `handle`, `url` | Shopify's product ID, the URL handle and the link to the product page. |
| `title`, `vendor`, `productType` | Product name, brand and type exactly as the store entered them. |
| `tags` | The store's tags, which often hold internal codes such as `allbirds::material => sugar`. |
| `price`, `maxPrice`, `currency` | Lowest and highest variant price, in the store's default currency. |
| `compareAtPrice`, `discountPercent`, `onSale` | Original price and percent off for the variant with the deepest markdown, such as `50` and `50` for sandals at $25 instead of $50. |
| `available`, `variantCount`, `availableVariants` | `true` when any variant can be bought, the number of variants, and how many of them are available. |
| `totalInventory` | Units in stock summed over the variants, with stock counts on and published by the store. |
| `options` | Option names and values, such as `Size` with `5` to `11`. Products without options have an empty list. |
| `variants` | One object per variant: `variantId`, `title`, `sku`, `barcode`, `option1` to `option3`, `price`, `compareAtPrice`, `available`, `inventoryQuantity` and `weightGrams`. |
| `image`, `images` | First image and every image, hosted on Shopify's CDN. |
| `description` | Product text with the HTML removed, when **Include descriptions** is on. |
| `createdAt`, `publishedAt`, `updatedAt` | Timestamps with the store's UTC offset, such as `2026-03-27T14:03:50-07:00`. |
| `change`, `previousPrice` | With a watch name, `new`, `price_drop`, `price_increase`, `back_in_stock` or `sold_out`, and the price in the previous run. |
| `checkedAt` | When the row was read, in UTC. |

With **One row per variant** on, each row is one variant: it keeps the product columns and adds `variantId`, `variantTitle`, `sku`, `barcode`, `option1` to `option3`, `inventoryQuantity` and `weightGrams`, while `variants`, `options`, `maxPrice`, `variantCount`, `availableVariants` and `totalInventory` are left out.

### How to scrape a Shopify store

1. Fill **Stores, collections or products** with one entry per line: a domain such as `allbirds.com`, a collection link such as `https://colourpop.com/collections/lips`, or a product link. Links with a market prefix such as `/en-gb/` are understood.
2. Cap the size with **Maximum products per store** (1,000 by default) and **Maximum rows**.
3. Keep only what you need with **Keywords (any of)**, which looks in the title, product type, vendor and tags, and with **Only products in stock** or **Only discounted products**.
4. Turn on **Include stock counts and barcodes** for units per variant and GTIN, EAN or UPC codes, **Include descriptions** for the product text, and **One row per variant** for a flat sheet.
5. Click **Start**. Products arrive on the **Output** tab page by page and download as CSV, Excel or JSON.

An API input that follows the discounted lips range of one store and returns only changes:

```json
{
  "stores": ["https://colourpop.com/collections/lips"],
  "onlyDiscounted": true,
  "includeStockAndBarcodes": true,
  "watchName": "lip-prices",
  "changesOnly": true
}
```

### Example output

A product link read on 11 October 2026 with stock counts and descriptions on: Allbirds women's flip flops at half price, with one of seven sizes left and 11 pairs of it in stock. `change` and `previousPrice` are `null` because the run had no watch name.

```json
{
  "store": "www.allbirds.com",
  "storeName": "Allbirds",
  "productId": 7340901859408,
  "handle": "womens-allbirds-flip-flop-dusty-pink",
  "url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink",
  "title": "Women's Allbirds Flip Flop - Dusty Pink",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": [
    "allbirds::carbon-score => undefined",
    "allbirds::cfId => color-a4d5d0f6620dc304306f503bf67bc5f9",
    "allbirds::complete => true",
    "allbirds::edition => limited",
    "allbirds::gender => womens",
    "allbirds::hue => pink",
    "allbirds::master => womens-allbirds-flip-flop",
    "allbirds::material => sugar",
    "allbirds::price-tier => tier-3",
    "allbirds::silhouette => zeffer",
    "loop::returnable => true",
    "OOS DNS",
    "shoprunner",
    "WC WAVE 1 TRUCK #1-#16 7.30.2026",
    "YBlocklist",
    "YGroup_ygroup_womens-allbirds-flip-flop"
  ],
  "currency": "USD",
  "price": 25,
  "maxPrice": 25,
  "compareAtPrice": 50,
  "discountPercent": 50,
  "onSale": true,
  "available": true,
  "variantCount": 7,
  "availableVariants": 1,
  "totalInventory": 11,
  "options": [
    {
      "name": "Size",
      "values": [
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11"
      ]
    }
  ],
  "variants": [
    {
      "variantId": 42146889039952,
      "title": "5",
      "sku": "A12513W050",
      "barcode": "196942293089",
      "option1": "5",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 455
    },
    {
      "variantId": 42146889072720,
      "title": "6",
      "sku": "A12513W060",
      "barcode": "196942293096",
      "option1": "6",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 492
    },
    {
      "variantId": 42146889105488,
      "title": "7",
      "sku": "A12513W070",
      "barcode": "196942293102",
      "option1": "7",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 503
    },
    {
      "variantId": 42146889138256,
      "title": "8",
      "sku": "A12513W080",
      "barcode": "196942293119",
      "option1": "8",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 516
    },
    {
      "variantId": 42146889171024,
      "title": "9",
      "sku": "A12513W090",
      "barcode": "196942293126",
      "option1": "9",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 592
    },
    {
      "variantId": 42146889203792,
      "title": "10",
      "sku": "A12513W100",
      "barcode": "196942293133",
      "option1": "10",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": true,
      "inventoryQuantity": 11,
      "weightGrams": 594
    },
    {
      "variantId": 42146889236560,
      "title": "11",
      "sku": "A12513W110",
      "barcode": "196942293140",
      "option1": "11",
      "option2": null,
      "option3": null,
      "price": 25,
      "compareAtPrice": 50,
      "available": false,
      "inventoryQuantity": 0,
      "weightGrams": 615
    }
  ],
  "image": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345",
  "images": [
    "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345",
    "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_BACK.png?v=1774646345",
    "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_TD.png?v=1774646346",
    "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_SOLE.png?v=1774646346",
    "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_PAIR_3Q.png?v=1774646345"
  ],
  "description": "Sun on your feet. Comfort underneath. Light, easy, and made for warm weather, these flip flops bring everyday comfort to sunny days, beach walks, and everything in between. Just slip them on and go.",
  "createdAt": "2026-03-27T14:03:50-07:00",
  "publishedAt": "2026-09-25T16:58:13-07:00",
  "updatedAt": "2026-10-11T05:46:55-07:00",
  "change": null,
  "previousPrice": null,
  "checkedAt": "2026-10-11T12:46:55.444Z"
}
```

### How much does it cost to scrape Shopify products?

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 products | $0.50 | $0.40 | $0.325 | $0.25 |

You pay only for the events in the table. There is no start fee, and compute time and proxies are included.

A store with 1,000 products costs $0.50 per run on the Free plan and $0.25 on Business, with or without stock counts and descriptions. In variant-row mode every variant is a charged row, so an Allbirds shoe with 13 sizes counts 13 times. Products removed by a filter, unchanged products in changes-only mode and stores that cannot be read are not charged.

### Limits

- Stock counts depend on the store. On 11 October 2026 Allbirds and ColourPop published units per variant, while Brooklinen did not: its `inventoryQuantity` stayed `null` and its barcodes came through.
- Stock counts and barcodes need one extra request per product. Stores limit request rates, so the actor waits as the store asks, slows down for that store and spends at most 8 minutes per store on these requests. A store that limits the requests 4 times in a row, or refuses access, is not read further in that run and gets the status `blocked` in the `SUMMARY` record with the number of products read. Products it does not reach keep their catalog data without counts, and the log and the `SUMMARY` record say how many.
- A store with a custom (headless) storefront has no public feed. The `SUMMARY` record names it with the reason and it adds no rows. A run in which no store could be read ends as failed.
- The actor reads each store's `robots.txt` first and follows it, also for every redirect. A store whose `robots.txt` disallows its product feed gets the status `robots_disallowed` in the `SUMMARY` record and adds no rows; nothing is charged for it. When `robots.txt` answers with a server error or not at all, the store is skipped in that run.
- Price alerts follow each product's lowest variant price, and stock alerts the product as a whole. A price change on a dearer variant, or one size selling out while others remain, does not set `change`. In variant-row mode `change` and `previousPrice` still describe the product.
- Removed products are listed only after a run that read the whole store. Runs over a collection or a product link, and runs stopped by a limit, report no removals.
- Some stores keep content entries in their catalog. In October 2026 Brooklinen's feed had items titled `Content: ...` priced at 0; their title and price make them easy to filter out.
- Prices come in the store's default currency only.

### Price and stock alerts

Name the watch in **Watch name (price and stock alerts)**, for example `lip-prices`, or save the input as a task, and schedule it daily. The first run records every product's price and availability. Later runs mark each product in `change` with `previousPrice`, and with **Return only changes since the last run** on, only new products, price drops, price increases, restocks and sell-outs are returned and charged. The `CHANGES` record counts each kind and lists products that left the store. When **Maximum rows** or the spending limit stops a run, the products it did not return keep their old price and availability in the snapshot, so the next run still reports them.

### FAQ

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

The actor reads the product feed at `/products.json` that standard Shopify storefronts serve to anyone, and for stock counts or product links, each product's JSON page. Prices, variants and stock describe goods for sale; customer, order and review data is never touched. Photos and descriptions belong to the store, and you are responsible for how you use them and the rest of the data.

#### How often can I check prices?

As often as your schedule runs. Each run reads the feed again, 250 products per request. On 11 October 2026 a run that read 250 Brooklinen products took 3 seconds, and one with stock counts for 11 products took 6 seconds.

### Related scrapers

- [Shopify Store Analyzer](https://apify.com/deriverge/shopify-store-analyzer)
- [Tech Stack Detector](https://apify.com/deriverge/tech-stack-detector)

### Support

This actor is built and maintained by deriverge s.r.o., a software company based in the Czech Republic. If a run fails or a field you need is missing, please open an issue in the **Issues** tab or write to us at info@deriverge.com. We respond in English and Czech. Runs can be scheduled in Apify Console or started from the **API** tab, which has examples for Python, JavaScript and cURL and works with Make, Zapier, n8n and the Apify MCP server. If the actor saves you time, a short review helps other people find it.

# Changelog

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

# Actor input Schema

## `stores` (type: `array`):

One per line: a store domain (allbirds.com), a collection URL (https://colourpop.com/collections/lips) or a product URL. Works with any Shopify store that shows its catalog publicly, which is most of them.

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

Stop after this many products in each store or collection.

## `includeStockAndBarcodes` (type: `boolean`):

Opens each product for its stock count per variant (when the store tracks stock), barcode (GTIN, EAN, UPC) and weight. One extra request per product, so slower; the price is the same.

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

Add the product description as plain text.

## `keywords` (type: `array`):

Keep only products whose title, type, vendor or tags contain one of these, for example "hoodie" or "vegan".

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

Skip products with no variant available.

## `onlyDiscounted` (type: `boolean`):

Keep only products with a compare-at price above the price.

## `rowPerVariant` (type: `boolean`):

Output each size or color as its own row with its SKU, price, availability and stock, instead of one row per product with a variants list. Handy for spreadsheets; every row is charged.

## `changesOnly` (type: `boolean`):

With a watch name or a saved task, return only new products, price drops and increases, and products back in stock or sold out.

## `watchName` (type: `string`):

Give the run a name such as "competitors" and schedule it. Every later run with the same name fills change and previousPrice on each product and lists removed products in the CHANGES record. Runs from a saved task are compared automatically.

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

Hard cap on returned rows in the run.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com"
  ],
  "maxProductsPerStore": 1000,
  "includeStockAndBarcodes": false,
  "includeDescription": false,
  "onlyAvailable": false,
  "onlyDiscounted": false,
  "rowPerVariant": false,
  "changesOnly": false,
  "maxItems": 20
}
```

# Actor output Schema

## `products` (type: `string`):

One row per product (or per variant) with prices, discounts, availability, stock, images and tags.

## `changes` (type: `string`):

Counts of new products, price changes and stock changes, and the list of products removed from each store.

## `summary` (type: `string`):

Products read per store and why a store could not be read.

# 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 = {
    "stores": [
        "allbirds.com"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/shopify-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 = {
    "stores": ["allbirds.com"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("deriverge/shopify-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 '{
  "stores": [
    "allbirds.com"
  ],
  "maxItems": 20
}' |
apify call deriverge/shopify-products-scraper --silent --output-dataset

```

## MCP server setup

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