# Shopify & WooCommerce Scraper – Prices & Stock Changes (`modzsenpai/store-catalog-tracker`) Actor

Export full product catalogs from any Shopify or WooCommerce store (public storefront endpoints, no login) and track price drops, price increases, restocks, sell-outs and removed products between runs.

- **URL**: https://apify.com/modzsenpai/store-catalog-tracker.md
- **Developed by:** [Rahul Neeraj Mody](https://apify.com/modzsenpai) (community)
- **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 & WooCommerce Scraper – Products, Prices & Stock Changes

Export the **full product catalog of any Shopify or WooCommerce store** and **track price drops, price increases, restocks, sell-outs and removed products** between runs. Paste store homepage URLs, press Start, and get a clean spreadsheet of products, variants, SKUs, prices, compare-at prices, stock status, images and tags. No login, no API key, no browser needed.

This Actor reads the stores' **public storefront endpoints** (Shopify `/products.json` and the WooCommerce Store API) — the same data any visitor's browser can see. It is fast, cheap and does not collect any customer or personal data.

### What can this Shopify & WooCommerce scraper do?

- **Auto-detects the platform** – Shopify or WooCommerce, per store.
- **Exports the whole catalog** – title, vendor/brand, product type/category, handle, URL, SKU, price, compare-at price, currency, availability, stock status, tags, image, created/updated dates.
- **Per product or per variant** – one row per product (lowest price, any-in-stock) or one row per size/colour variant.
- **Price & stock change tracking** – every row gets a `changeType`: `new`, `price_up`, `price_down`, `back_in_stock`, `out_of_stock`, `removed` or `unchanged`, plus `previousPrice`.
- **Only-changes mode** – schedule it daily and get (and pay for) only the products that changed.
- **Collection / category filter** – scrape one Shopify collection or one WooCommerce category.
- **Polite by design** – sequential requests per store, delays, retries with exponential backoff and `Retry-After` support for HTTP 429.

### Use cases

- **Competitor price monitoring** – schedule daily runs on competitors' stores and get alerted on every price drop or increase.
- **Dropshipping & product research** – find what a store sells, its price points, best-selling categories, and newly added products.
- **Stock / restock alerts** – know when a sold-out product is back in stock (or a competitor runs out).
- **Catalog migration & backups** – export a Shopify or WooCommerce catalog to CSV, Excel or JSON.
- **Market and assortment analysis** – compare vendors, product types, discount depth (`compareAtPrice` vs `price`) across many stores.
- **MAP / reseller compliance** – check that resellers' prices stay above your minimum advertised price.

### How to use it

1. Add one or more store URLs to **Store URLs** (e.g. `https://www.allbirds.com`).
2. Optionally set **Collection / category**, **Max products per store** and **One row per variant**.
3. Keep **Track price & stock changes** on. The first run records a baseline (all rows are `new`).
4. [Schedule](https://docs.apify.com/platform/schedules) the Actor (e.g. daily) and turn on **Output only changed products** to receive just the changes.
5. Download results as JSON, CSV, Excel, or connect them to Google Sheets, Slack, Make, Zapier or webhooks.

### Input example

```json
{
    "startUrls": [
        { "url": "https://www.allbirds.com" },
        { "url": "https://colourpop.com" },
        { "url": "https://www.filamentive.com" }
    ],
    "platform": "auto",
    "collection": "",
    "maxProductsPerStore": 1000,
    "includeVariants": false,
    "trackChanges": true,
    "onlyChanges": false
}
```

| Field | Description |
|---|---|
| `startUrls` | Store homepages. Product/collection URLs also work (the store root is used). |
| `platform` | `auto` (default), `shopify` or `woocommerce`. |
| `collection` | Shopify collection handle (e.g. `mens`) or WooCommerce category slug/ID. |
| `maxProductsPerStore` | Product cap per store (default 1000). |
| `includeVariants` | `true` = one row per variant, `false` = one row per product. |
| `trackChanges` | Compare with the previous run's snapshot (default `true`). |
| `onlyChanges` | Output only rows whose `changeType` is not `unchanged`. |
| `requestDelayMs` | Pause between page requests to one store (default 700 ms). |

### Output example

```json
{
    "store": "https://www.allbirds.com",
    "platform": "shopify",
    "productId": "7340901859408",
    "variantId": null,
    "title": "Women's Allbirds Flip Flop - Dusty Pink",
    "vendor": "Allbirds",
    "productType": "Shoes",
    "handle": "womens-allbirds-flip-flop-dusty-pink",
    "url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink",
    "sku": "A12513W050",
    "price": 25,
    "maxPrice": 25,
    "compareAtPrice": 50,
    "currency": "USD",
    "available": true,
    "stockStatus": "in_stock",
    "tags": ["allbirds::gender => womens", "shoprunner"],
    "imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png",
    "variantCount": 7,
    "createdAt": "2026-03-27T14:03:50-07:00",
    "updatedAt": "2026-10-15T21:27:17-07:00",
    "changeType": "price_down",
    "changes": ["price_down"],
    "previousPrice": 30,
    "previousAvailable": true,
    "scrapedAt": "2026-10-16T04:27:14.391Z"
}
```

The dataset has three ready-made views: **Products**, **Price & stock changes**, and **All fields**. A run summary (products, rows, change counts per store) is saved to the `SUMMARY` record in the run's key-value store.

If a store cannot be read (not Shopify/WooCommerce, API disabled, or blocked), you are not charged for it: the reason is listed under `errors` in the run's `SUMMARY` record (Storage → Key-value store), and in the run log.

#### Change types

| `changeType` | Meaning |
|---|---|
| `new` | Not present in the previous snapshot (every row on the first run). |
| `price_up` / `price_down` | Price changed since the last run; see `previousPrice`. |
| `back_in_stock` / `out_of_stock` | Availability flipped. If price also changed, `changes` lists both. |
| `removed` | Was in the last snapshot but is gone now (only reported when the full catalog was scraped). |
| `unchanged` | Same price and availability. |

Snapshots are stored per store (and per collection / variant mode) in the named key-value store **`store-catalog-tracker-snapshots`** in your Apify account, so tracking works across scheduled runs. Delete a record there to reset the baseline.

### Pricing

Pay per result: **$0.001 per product row** in the dataset ($1 per 1,000 products). Error rows are free, and rows skipped by **Output only changed products** are not charged. Set a maximum cost per run in the run options – the Actor stops cleanly when your budget is reached (and doesn't advance the change-tracking baseline, so you never miss a change).

Example: tracking 3 competitor stores with 1,000 products each, daily, with only-changes on – the first run costs ~$3, then you pay only for the products that changed.

### FAQ

**Is it legal to scrape Shopify and WooCommerce stores?**
This Actor only reads public product data that stores expose to every visitor through their storefront endpoints. It collects no personal data. You are still responsible for complying with the target site's terms and applicable laws.

**Why did a store return an error?**
Some stores disable `/products.json` or the WooCommerce Store API, sit behind bot protection (Cloudflare, etc.), or use another platform (Magento, BigCommerce, Wix…). Those stores can't be read via public endpoints.

**How many products can it get?**
Shopify's public endpoint paginates up to 100 pages × 250 = 25,000 products per store (or per collection – use collections to go beyond). WooCommerce has no fixed cap.

**Does it get inventory quantities?**
Shopify's public endpoint exposes only availability (in/out of stock), not quantities. WooCommerce exposes `lowStockRemaining` when the store shows "only X left".

**Why is currency empty?**
Shopify currency comes from the store's `/meta.json` (base currency); a few stores hide it. WooCommerce always returns a currency code.

**Do I need proxies?**
Usually not – requests are light and polite. Heavily protected stores may block datacenter IPs.

**Can I monitor only one collection?**
Yes – set **Collection / category**. Each collection keeps its own change-tracking snapshot.

### Limitations

- Shopify page-based pagination stops at page 100 (25,000 products per endpoint).
- WooCommerce Store API does not expose created/updated dates or vendor unless the store uses WooCommerce Brands.
- Prices are those shown to an anonymous visitor in the store's default currency/market (no currency conversion, no B2B/member pricing).
- `removed` is only reported when the whole catalog fits under `maxProductsPerStore`.

# Actor input Schema

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

Homepage URLs of the Shopify or WooCommerce stores to scrape, e.g. https://www.allbirds.com. Product or collection URLs also work – the Actor uses the store root.

## `platform` (type: `string`):

Leave on Auto-detect unless you know the platform. Auto tries Shopify (/products.json) first, then the WooCommerce Store API.

## `collection` (type: `string`):

Only scrape one collection. Shopify: collection handle (the part after /collections/, e.g. 'mens'). WooCommerce: product category slug or numeric ID. Applied to every store in the list.

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

Stop after this many products per store. With 'One row per variant' a product can produce several rows. Removal detection only works when the whole catalog fits under this limit.

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

Output one row per variant (size/colour) with its own SKU, price and stock. When off, you get one row per product with the lowest variant price and 'available' = any variant in stock.

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

Save a snapshot of each store and compare it on the next run. Each row gets changeType: new, price_up, price_down, back_in_stock, out_of_stock, removed or unchanged. Snapshots are kept in the named key-value store 'store-catalog-tracker-snapshots' in your account.

## `onlyChanges` (type: `boolean`):

Skip rows whose changeType is 'unchanged' (you are not charged for skipped rows). Ideal for scheduled monitoring. Note: on the very first run every product is 'new'.

## `requestDelayMs` (type: `integer`):

Pause between page requests to the same store. Keep it polite – the default works for most stores.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.allbirds.com"
    },
    {
      "url": "https://colourpop.com"
    }
  ],
  "platform": "auto",
  "maxProductsPerStore": 300,
  "includeVariants": false,
  "trackChanges": true,
  "onlyChanges": false,
  "requestDelayMs": 700
}
```

# Actor output Schema

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

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("modzsenpai/store-catalog-tracker").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": [
        { "url": "https://www.allbirds.com" },
        { "url": "https://colourpop.com" },
    ],
    "maxProductsPerStore": 300,
}

# Run the Actor and wait for it to finish
run = client.actor("modzsenpai/store-catalog-tracker").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": [
    {
      "url": "https://www.allbirds.com"
    },
    {
      "url": "https://colourpop.com"
    }
  ],
  "maxProductsPerStore": 300
}' |
apify call modzsenpai/store-catalog-tracker --silent --output-dataset

```

## MCP server setup

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

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/XoioiJZRCX8n6C22W/builds/O1frYmwjinB4UzNI0/openapi.json
