# Store Catalog & Price Tracker: Shopify and WooCommerce (`agenttoolworks/shopify-woocommerce-catalog-tracker`) Actor

Export the full product catalog of any Shopify or WooCommerce store (prices, compare-at prices, variants, SKUs, stock, images, tags) into one normalized dataset, and track price drops, restocks, new and removed products between runs.

- **URL**: https://apify.com/agenttoolworks/shopify-woocommerce-catalog-tracker.md
- **Developed by:** [AgentToolWorks](https://apify.com/agenttoolworks) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 product returneds

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

### What is Store Catalog & Price Tracker?

Store Catalog & Price Tracker is a **Shopify scraper and WooCommerce scraper** in one Actor. Paste the URLs of any Shopify or WooCommerce stores, mixed in one list, and get every product back in the same clean format: titles, prices, sale prices, variants, SKUs, **barcodes (GTIN/UPC/EAN)**, stock, weights, options, images with their sizes and alt text, tags, categories and dates.

Turn on **change tracking** and schedule it, and it becomes a **competitor price monitor**: each run outputs only what changed since the previous one (price drops, price increases, back in stock, out of stock, new products, removed products).

![Store Catalog & Price Tracker: Shopify and WooCommerce store URLs in, one normalized product dataset out](https://agenttoolworks.com/apify/storecatalog-overview.png)

### What data can you extract from Shopify and WooCommerce stores?

One row per product, with its variants nested (or one row per variant, see [Output](#output)). Every row has the same fields whichever platform the store runs on.

| Product data | Variant data | Store data |
|---|---|---|
| Title, handle, URL | Variant title and options (Size, Color) | Platform detected (Shopify or WooCommerce) |
| Vendor or brand, product type | SKU and barcode (GTIN, UPC, EAN) | Store domain and URL |
| Categories and tags | Price and compare-at (sale) price | Currency |
| Lowest and highest price, compare-at price, on sale | In stock, stock quantity where public | Collection or category read |
| Options with all their values | Backorder policy | Run summary per store |
| Images: URL, width, height, alt text, linked variants | Weight in grams | |
| Description as plain text and as HTML | Requires shipping, taxable | |
| Rating and review count (WooCommerce) | Variant image and position | |
| Created, updated and published dates | Created and updated dates (Shopify) | |

Everything comes from the store's own public storefront: Shopify's `products.json` and Ajax product endpoints, and the public WooCommerce Store API. No login, no account, no customer data.

### What can you use Shopify and WooCommerce product data for?

- **Competitor price monitoring**: track price changes and discounts across many stores, daily or hourly.
- **Restock alerts**: know when a sold-out product or size is back in stock.
- **Product research and dropshipping**: see what a brand sells, at what price, and what it launches.
- **Price comparison and product feeds**: match products across stores by barcode (GTIN) and build a feed.
- **Catalog audits**: missing SKUs or barcodes, out-of-stock share, sale depth, image alt text coverage.
- **Data for AI agents and pipelines**: one predictable JSON schema for any Shopify or WooCommerce store.

### How to scrape a Shopify or WooCommerce store

1. Click **Try for free** (or **Start**) on this page and sign in to Apify.
2. Paste one or more store URLs in **Store URLs**: a home page (`https://www.allbirds.com`), a Shopify collection (`https://www.allbirds.com/collections/mens`) or a WooCommerce category page.
3. Set **Max products per store**. You pay per product returned, so start small to look at the data.
4. Turn on **Include stock levels, barcodes and image alt text** if you need them (Shopify; slower).
5. Click **Start**. When the run finishes, open the **Output** tab, pick the **Products** or **Variants** view, and export as JSON, CSV, Excel or XML, or read it through the API.

![The Actor's input form in Apify Console: store URLs, max products per store, change tracking and options](https://agenttoolworks.com/apify/storecatalog-console-input.jpg)

### Input

| Field | What it does |
|---|---|
| `storeUrls` | Shopify or WooCommerce store URLs, mixed. A home page, a Shopify collection page or a WooCommerce category page. Up to 100 stores per run. |
| `maxProductsPerStore` | Stop after this many products per store (default 1,000, up to 100,000). You pay per product returned. |
| `trackChanges` | Save a snapshot per store and output what changed since the last run. |
| `outputProducts` | Turn off to output only the changes (for scheduled monitoring). |
| `collection` | A Shopify collection handle or WooCommerce category slug, for every store whose URL does not already name one. |
| `includeInventory` | Shopify: read each product's public Ajax endpoint for stock counts, barcodes, backorder policy and image alt text. Slower: at most 500 products per store per run. |
| `includeDescription` | Add `description` (plain text) and `descriptionHtml` (the store's HTML), plus `shortDescription` on WooCommerce. |
| `proxy` | `none` (default) or `datacenter` (Apify Proxy) if a store rate-limits your runs. |

Example: one Shopify collection and one WooCommerce store, with barcodes and stock.

```json
{
  "storeUrls": ["https://www.allbirds.com/collections/mens", "https://woocommerce.com"],
  "maxProductsPerStore": 500,
  "includeInventory": true
}
```

Example: a daily price and stock monitor that outputs only changes.

```json
{
  "storeUrls": ["https://competitor-one.com", "https://competitor-two.com"],
  "maxProductsPerStore": 20000,
  "trackChanges": true,
  "outputProducts": false
}
```

### Output

The dataset has three ready-made views in Apify Console and in the API:

- **Products**: one row per product.
- **Variants (one row each)**: one row per variant, with SKU, barcode, price, stock and weight. The same as adding `unwind=variants` to the dataset API.
- **Changes**: the change rows, when change tracking is on.

![Products view of a real run: Allbirds and Kith products with image, price, stock, vendor, type and counts](https://agenttoolworks.com/apify/storecatalog-console-products.jpg)

![Variants view of a real run: one row per size with SKU, barcode, price, stock and weight in grams](https://agenttoolworks.com/apify/storecatalog-console-variants.jpg)

A Shopify product row from a real run (variants and images shortened):

```json
{
  "rowType": "product",
  "platform": "shopify",
  "storeDomain": "www.allbirds.com",
  "storeUrl": "https://www.allbirds.com",
  "productId": "7340901859408",
  "handle": "womens-allbirds-flip-flop-dusty-pink",
  "title": "Women's Allbirds Flip Flop - Dusty Pink",
  "url": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "categories": [],
  "tags": ["allbirds::complete => true", "allbirds::edition => limited"],
  "price": 25,
  "priceMax": 25,
  "compareAtPrice": 50,
  "onSale": true,
  "currency": "USD",
  "available": true,
  "requiresShipping": true,
  "sku": null,
  "inventoryQuantity": 13,
  "variantCount": 7,
  "options": [{ "name": "Size", "position": 1, "values": ["5", "6", "7", "8", "9", "10", "11"] }],
  "variants": [
    {
      "variantId": "42146889039952",
      "variantTitle": "5",
      "variantSku": "A12513W050",
      "variantOptions": { "Size": "5" },
      "variantPrice": 25,
      "variantCompareAtPrice": 50,
      "variantAvailable": false,
      "variantInventory": 0,
      "variantAllowsBackorder": false,
      "variantBarcode": "196942293089",
      "variantWeightGrams": 455,
      "variantRequiresShipping": true,
      "variantTaxable": true,
      "variantImage": null,
      "variantPosition": 1,
      "variantCreatedAt": "2026-03-27T14:03:50-07:00",
      "variantUpdatedAt": "2026-09-30T17:03:07-07:00"
    }
  ],
  "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"],
  "imageDetails": [
    {
      "src": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png?v=1774646345",
      "alt": null,
      "width": 4000,
      "height": 4000,
      "position": 1,
      "variantIds": []
    }
  ],
  "imageCount": 5,
  "rating": null,
  "reviewCount": null,
  "createdAt": "2026-03-27T14:03:50-07:00",
  "updatedAt": "2026-09-30T17:03:07-07:00",
  "publishedAt": "2026-09-25T16:58:13-07:00",
  "collection": null,
  "scrapedAt": "2026-10-01T00:03:06.433Z"
}
```

A WooCommerce product row has exactly the same fields. Where the WooCommerce Store API publishes nothing, the field is `null` rather than guessed: no creation or update dates, no barcodes, no shipping or tax flags. In exchange, WooCommerce rows carry `rating` and `reviewCount`.

```json
{
  "rowType": "product",
  "platform": "woocommerce",
  "storeDomain": "woocommerce.com",
  "productId": "18734006912442",
  "title": "PH PostNord Shipping with Print Label",
  "url": "https://woocommerce.com/products/ph-postnord-shipping-with-print-label/",
  "categories": ["WooCommerce extensions", "Shipping, delivery and fulfillment", "Label printing"],
  "price": 99,
  "currency": "USD",
  "available": true,
  "variantCount": 1,
  "imageCount": 5,
  "rating": null,
  "reviewCount": 0
}
```

Prices are numbers in the row's `currency` (`25` means 25.00), so they sort and chart directly. Weights are always grams, whatever unit the store uses.

#### Change rows

With `trackChanges` on, each run also outputs one row per change since the previous run. A real one: two sizes of a sneaker came back in stock at Kith between two runs 46 minutes apart.

```json
{
  "rowType": "change",
  "changeType": "back_in_stock",
  "platform": "shopify",
  "storeDomain": "kith.com",
  "storeUrl": "https://kith.com",
  "productId": "8286820892800",
  "variantId": "45144949096576",
  "title": "adidas x Entire Studios Adizero Adios 9 - Core Black / Feather Grey / Mist Stone",
  "variantTitle": "7",
  "handle": "aakj2303",
  "url": "https://kith.com/products/aakj2303",
  "price": 180,
  "oldPrice": null,
  "newPrice": null,
  "available": true,
  "currency": "USD",
  "detectedAt": "2026-10-01T01:05:34.945Z",
  "previousCheckAt": "2026-10-01T00:19:56.772Z"
}
```

`changeType` is one of `new_product`, `removed_product`, `price_up`, `price_down`, `back_in_stock`, `out_of_stock`. Price changes fill `oldPrice` and `newPrice` (compared to the cent); stock changes leave them `null` and give the current `price`. A change on a single variant carries its `variantId` and `variantTitle`; a new or removed product has them `null`.

#### Run summary

The run's key-value store holds a `SUMMARY` record with the status of each store: `ok`, `partial` (stopped part way, with the reason; the products read are kept), `blocked` (the store refused the request: a bot challenge or an HTTP 403), `unsupported` (not Shopify, or WooCommerce with its Store API closed), `refused` (not a public https address) or `failed`. It also says whether a store had more products than your limit, so a cut catalog is never silent.

### How to monitor competitor prices with a schedule

1. Run the Actor once with **Track changes since the last run** on. This first run saves a baseline for each store and reports no changes.
2. On the input page, click **Save as a new task**. Turn off **Output product rows** in the task if you only want the changes.
3. Open **Schedules**, create a schedule (daily, or every few hours) and add the task to it.
4. Every scheduled run outputs only what changed since the previous run. Add an integration (email, Slack, Google Sheets, a webhook, Make, Zapier or n8n) to be notified when a run has rows.

Snapshots are kept in your account's named key-value store `storecatalog-state`, one record per store and per collection or category. Delete a record to start that store over from a new baseline.

Two things worth knowing:

- **Cover the whole catalog** (`maxProductsPerStore` above the store's size) to detect new and removed products. When a run stops at your limit, a product it did not reach may still exist, so the run reports only price and stock changes for that store and says so in the summary.
- If a run stops at your spending limit before every change is delivered, that store's snapshot is not updated, so the next run reports those changes again instead of losing them.

### How much does it cost to scrape a Shopify store?

Pay per event, no subscription, platform usage included:

| Event | Price |
|---|---|
| Product returned | **$1.50 per 1,000 products** ($0.0015 each) |
| Change detected | **$0.005 per change** |
| Run start | $0.00005 |

Examples:

- **A 1,000 product store**: $1.50.
- **Apify's free plan** includes $5 of monthly usage ([apify.com/pricing](https://apify.com/pricing)), enough for about **3,300 products** a month.
- **A daily monitor** of a 2,000 product store with **Output product rows** off pays only for changes: a day with 12 price or stock changes costs $0.06.

A run that finds nothing to output costs only the start fee. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.

### Use the Actor through the Apify API

Run it and get the products in one HTTP call (replace `YOUR_APIFY_TOKEN`, from **Settings > API & Integrations** in Apify Console):

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

Node.js, with the official [`apify-client`](https://docs.apify.com/api/client/js/):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('agenttoolworks/shopify-woocommerce-catalog-tracker').call({
    storeUrls: ['https://www.allbirds.com', 'https://woocommerce.com'],
    maxProductsPerStore: 100,
    includeInventory: true,
});
// One item per variant instead of per product: add { unwind: 'variants' }.
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((p) => [p.title, p.price, p.currency]));
```

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

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("agenttoolworks/shopify-woocommerce-catalog-tracker").call(
    run_input={"storeUrls": ["https://www.allbirds.com"], "maxProductsPerStore": 100}
)
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["title"], product["price"], product["currency"])
```

The **API** tab on this page has ready-made snippets for every language and endpoint.

### FAQ

#### Does it work on every Shopify store?

On nearly every one: `products.json` is part of every Shopify storefront. A store behind an extra bot challenge is reported as `blocked` in the run summary.

#### Does it work on every WooCommerce store?

On WooCommerce stores whose Store API is open, which is the default in recent WooCommerce versions. Stores that close it are reported as `unsupported`, and stores behind a firewall as `blocked`.

#### How fast is it?

Measured on Apify: Allbirds (692 products, 3 catalog requests) in 3 seconds, the first 5,000 Kith products in 29 seconds. With **Include stock levels, barcodes and image alt text**, each product needs one more request, so plan about 1.5 minutes per 300 products (measured: 300 Allbirds products in 90 seconds). WooCommerce speed depends mostly on the store's server.

#### Can I get stock quantities and barcodes?

Shopify: turn on **Include stock levels, barcodes and image alt text**. Barcodes come back for every product that has one; stock counts only for stores that track inventory publicly. WooCommerce publishes a stock count only when stock is low, and never barcodes.

#### Can I get one row per variant, for a spreadsheet?

Yes: choose the **Variants (one row each)** view before exporting, or add `unwind=variants` to the dataset items API.

#### Can I scrape only one collection or category?

Yes: paste the collection URL (`/collections/sale`) or the WooCommerce category URL, or set **Collection or category** to apply the same one to every store.

#### Can I use it with Make, Zapier, n8n or Google Sheets?

Yes, through Apify's integrations: the Actor's dataset works with every Apify integration, and a webhook can fire when a run finishes.

#### Can AI agents use it through MCP?

Yes. Apify's hosted MCP server exposes Store Actors to MCP clients such as Claude or Cursor: `https://mcp.apify.com?tools=agenttoolworks/shopify-woocommerce-catalog-tracker`.

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

The Actor reads only the product information a store publishes to every visitor, through the storefront's public endpoints, without logging in, and it collects no personal data. As with any data, how you use it is your responsibility.

#### Something is missing or broken?

Open an issue in the **Issues** tab: we read every one, and fields asked for by users get added.

### Other Actors by AgentToolWorks

- [JobsRadar: multi-board job search](https://apify.com/agenttoolworks/jobsradar-multi-board-job-search): open roles from Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Personio and Pinpoint, deduplicated into one list.

Built by [AgentToolWorks](https://agenttoolworks.com). Not affiliated with Shopify or with WooCommerce.

# Actor input Schema

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

Shopify or WooCommerce stores, mixed freely. The platform is detected for each one. Paste the home page (allbirds.com), or a collection or category page (allbirds.com/collections/mens, shop.example.com/product-category/shoes/) to read only that part of the catalog.

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

Stop reading a store after this many products. You are charged per product returned. When a store has more, the run summary says so, and change tracking skips new/removed product detection for that store until a run covers the whole catalog.

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

Save a snapshot of each store in the named key-value store 'storecatalog-state' and output what changed since the previous run: new products, removed products, price up, price down, back in stock, out of stock. The first run for a store only saves the baseline. Schedule this Actor to monitor stores.

## `outputProducts` (type: `boolean`):

Turn off for monitoring runs where you only want the changes: products are still read and compared, but only change rows are output (and charged).

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

Optional. A Shopify collection handle or WooCommerce category slug applied to every store whose URL does not already name one, for example 'sale'.

## `includeInventory` (type: `boolean`):

Shopify: read each product's public Ajax endpoint, which adds stock counts where the store tracks them, barcodes (GTIN/UPC/EAN), the backorder policy and image alt text (slower: at most 500 products per store per run, 150 ms apart). WooCommerce publishes a count only when stock is low, and that is always included.

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

Add the description to each row twice: 'description' as clean plain text, 'descriptionHtml' as the store's HTML (plus 'shortDescription' on WooCommerce). Off by default to keep rows compact.

## `proxy` (type: `string`):

Most stores answer directly. Use Apify datacenter proxy if a store rate-limits your runs. Stores behind a bot challenge are reported as blocked in the run summary; this Actor does not try to get past them.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com",
    "https://www.allbirds.com/collections/mens",
    "https://woocommerce.com"
  ],
  "maxProductsPerStore": 10,
  "trackChanges": false,
  "outputProducts": true,
  "includeInventory": false,
  "includeDescription": false,
  "proxy": "none"
}
```

# Actor output Schema

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

No description

## `variants` (type: `string`):

No description

## `summary` (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",
        "https://woocommerce.com"
    ],
    "maxProductsPerStore": 10
};

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

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

```

## MCP server setup

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