# Shopify Store Products & Price Change Monitor (`mersetti/shopify-products-monitor`) Actor

Export all products, variants, prices, stock and SKUs from any Shopify store, and monitor competitors for price drops, price increases, new products, back-in-stock and sold-out changes.

- **URL**: https://apify.com/mersetti/shopify-products-monitor.md
- **Developed by:** [Max Prozapas](https://apify.com/mersetti) (community)
- **Categories:** E-commerce, Lead generation, Automation
- **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.

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 Products & Price Change Monitor — Scrape Shopify Products, Prices, Stock & SKUs

Export the **full product catalog of any Shopify store** (products, variants, SKUs, prices, compare-at prices,
discounts, stock availability, images, tags) and **monitor competitors for price changes**. Paste store URLs
and get clean, structured data, with no login, API key or browser needed.

**Monitor mode** remembers every price and stock status between runs and outputs **only what changed**:
new products, price drops, price increases, back-in-stock, sold-out and removed products. Schedule it daily
and get a competitor price-change feed. You pay only for the changes.

### Why this Shopify scraper

- **Any Shopify store**: works with millions of stores (Allbirds, Kith, Gymshark, ColourPop, ...), including
  many **headless Shopify / Hydrogen** stores. The Actor finds the shop's `*.myshopify.com` domain automatically.
- **Variant-level data**: one row per SKU (size/color) with its own price, compare-at price, discount % and stock.
  Or switch to one row per product with min/max price and availability across variants.
- **Price change monitoring** with a free, silent baseline run. Price drop / price increase / back in stock /
  out of stock / new / removed, each can be turned on or off.
- **Collections**: paste a collection URL (`/collections/sale`) to scrape or monitor just that collection.
- **Fast and light**: reads Shopify's public storefront JSON (250 products per request). A 1,000-product store takes seconds.
- **Cheap**: pay per result, no monthly rental. In monitor mode you pay only for changes.

### Use cases

- **Competitor price monitoring**: track competitor prices and discounts daily and react to price drops.
- **Dropshipping & product research**: export winning stores' full catalogs, prices, vendors and new arrivals.
- **MAP compliance**: brands can watch resellers' Shopify stores for prices below the minimum advertised price.
- **Back-in-stock alerts**: get notified when a sold-out product or size is available again.
- **Catalog export**: download any Shopify store's product list to CSV, Excel, JSON, Google Sheets or your database.
- **Market & assortment analysis**: new product launches, sale depth, product types and vendors across stores.

### Input

| Field | Description |
|---|---|
| `stores` | Store URLs or domains. Required. Any page works, e.g. `https://www.allbirds.com`, `allbirds.com`, `weareallbirds.myshopify.com`. A collection URL (`https://www.allbirds.com/collections/mens`) limits the run to that collection. |
| `maxProductsPerStore` | Max products per store or collection (default 1000, up to 25,000). |
| `outputLevel` | `variant` (one row per SKU, default) or `product` (one row per product). |
| `includeDescription` | Add plain-text product description. |
| `keywords` | Keep products whose title, product type, vendor or tags contain any keyword. |
| `onlyAvailable` | Only in-stock variants/products. |
| `monitorMode` | Output only changes since the previous run. |
| `monitorId` | Separate saved state per watchlist (default `default`). |
| `baselineOnFirstRun` | First run for a store saves its catalog silently (no output, no cost). |
| `reportNewProducts`, `reportPriceDrops`, `reportPriceIncreases`, `reportBackInStock`, `reportOutOfStock` | Change types to output (default on). |
| `reportRemoved` | Also output items that disappeared from the store (default off). |

#### Example input: catalog export

```json
{
  "stores": ["https://www.allbirds.com", "https://www.gymshark.com", "https://kith.com/collections/sale"],
  "maxProductsPerStore": 5000,
  "outputLevel": "variant"
}
```

#### Example input: daily competitor price monitor

```json
{
  "stores": ["https://www.allbirds.com", "https://www.gymshark.com"],
  "keywords": ["runner", "leggings"],
  "monitorMode": true,
  "monitorId": "competitors",
  "reportPriceDrops": true,
  "reportBackInStock": true,
  "reportRemoved": true
}
```

### Output

Variant level (default), one row per SKU:

```json
{
  "store": "www.allbirds.com",
  "productId": 7340901859408,
  "variantId": 42146889039952,
  "title": "Women's Allbirds Flip Flop - Dusty Pink",
  "variantTitle": "5",
  "handle": "womens-allbirds-flip-flop-dusty-pink",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": ["allbirds::gender => womens", "allbirds::hue => pink"],
  "sku": "A12513W050",
  "options": { "Size": "5" },
  "price": 25,
  "compareAtPrice": 50,
  "discountPercent": 50,
  "currency": "USD",
  "available": false,
  "productUrl": "https://www.allbirds.com/products/womens-allbirds-flip-flop-dusty-pink?variant=42146889039952",
  "imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12513_26Q2_Allbirds-Flip-Flop-Dusty-Pink_PDP_LEFT.png",
  "createdAt": "2026-03-27T21:03:50.000Z",
  "updatedAt": "2026-10-13T22:22:18.000Z",
  "publishedAt": "2026-09-25T23:58:13.000Z",
  "scrapedAt": "2026-10-14T08:00:00.000Z"
}
```

In monitor mode each row also has:

```json
{
  "changeType": "priceDrop",
  "changes": ["priceDrop", "backInStock"],
  "previousPrice": 150,
  "previousAvailable": false,
  "firstSeenAt": "2026-10-01T08:00:00.000Z"
}
```

`changeType` is one of `new`, `priceDrop`, `priceIncrease`, `backInStock`, `outOfStock`, `removed`. When several
changes happen at once, `changeType` holds the first one and `changes` lists them all.

Product level (`outputLevel: "product"`) returns one row per product with `minPrice`, `maxPrice`,
`maxCompareAtPrice`, `maxDiscountPercent`, `anyAvailable`, `variantsCount`, `availableVariantsCount` and `options`.
In product-level monitoring, price changes refer to `minPrice` and stock changes to `anyAvailable`.

`currency` comes from the store itself (its storefront cart). If the store does not report it, the field is `null`.
The Actor never guesses it.

A per-store report (products fetched, matched, output, API domain used, currency, errors) is saved to the `SUMMARY`
record of the run's key-value store. Stores that are not Shopify, or that disabled `products.json`, are listed there
with the error `Not a Shopify store or products.json disabled`, and the run continues with the other stores.

### How monitoring works

- The state is saved in the named key-value store `shopify-monitor` under `state-<monitorId>` (gzipped JSON).
- The first time a store or collection is monitored, its catalog is saved silently (if `baselineOnFirstRun` is on).
  This also applies to stores you add to an existing watchlist later.
- **Removed** is reported only for stores read completely in the current run. A store that fails, is cut by
  `maxProductsPerStore` or blocks the run never produces false "removed" alerts.
- If the run's cost limit is reached, unreported changes stay pending and are reported in the next run.
- Changing `outputLevel` for an existing `monitorId` starts a new baseline.

### Pricing

Pay per result: a small fee per item in the dataset. A full catalog export costs one result per variant
(or per product with `outputLevel: "product"`). In monitor mode you pay only for detected changes, so a daily watch on
dozens of competitor stores usually costs cents per day. Set a maximum cost per run, and the Actor stops cleanly when it is reached.

### Tips

- **Scheduling**: enable `monitorMode`, create a Schedule (e.g. daily 08:00) and connect an integration (email,
  Slack, Google Sheets, Zapier, Make, webhook) to receive only changes.
- **Cheaper exports**: use `outputLevel: "product"` when you don't need sizes/colors, and `keywords` to narrow down.
- **Several watchlists**: use a different `monitorId` for each.
- **A store failed?** Check `SUMMARY`. Some stores are not on Shopify, are password-protected, or block
  the public product feed.

### Limitations

- Only products published to the store's online storefront are visible. Exact inventory quantities are not public,
  only in-stock / out-of-stock.
- Shopify serves at most 25,000 products per store or collection through this endpoint.
- Prices are those of the store's default market/currency. Region-specific prices of Shopify Markets are not covered.

### Legal

The Actor reads only public storefront data that Shopify stores publish for every visitor (`/products.json`).
It does not log in, bypass protections, or collect personal data. Product texts and images remain the
property of their owners. Make sure your use of the data complies with applicable laws and the stores' terms.

### Related tools

- [Career Sites Jobs Feed (11 ATS)](https://apify.com/mersetti/ats-jobs-feed)
- [Substack to Markdown for AI & RAG](https://apify.com/mersetti/substack-to-markdown)
- Guide: [Shopify products.json on GitHub](https://github.com/Mersetti/shopify-products-json-guide)

# Actor input Schema

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

Store URLs or domains. Any page of the store works (https://www.allbirds.com, allbirds.com, weareallbirds.myshopify.com). A collection URL (https://www.allbirds.com/collections/mens) limits the run to that collection.

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

Maximum number of products (after keyword filter) read from each store or collection. Shopify serves at most 25,000 products per store this way.

## `outputLevel` (type: `string`):

"variant": one row per variant (size/color) with its own SKU, price and stock. "product": one row per product with min/max price and availability across variants.

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

Adds the plain-text product description (descriptionText). No extra requests.

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

Keep products whose title, product type, vendor or tags contain ANY of these words (case-insensitive). Empty = all products.

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

Output only variants (or products) that are currently available. In monitor mode, out-of-stock events are still reported.

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

Remembers prices and stock between runs and outputs only changes: new products, price drops/increases, back in stock, out of stock and (optionally) removed products. Schedule the Actor (e.g. daily) to get a change feed. You pay only for changes.

## `monitorId` (type: `string`):

Name of the saved state. Use different IDs for different watchlists.

## `baselineOnFirstRun` (type: `boolean`):

The first time a store is monitored, only save its current catalog (no output, no cost), so later runs report real changes. Applies to stores added to an existing monitor, too.

## `reportNewProducts` (type: `boolean`):

Output items not seen in previous runs (changeType = new).

## `reportPriceDrops` (type: `boolean`):

Output items whose price went down (changeType = priceDrop).

## `reportPriceIncreases` (type: `boolean`):

Output items whose price went up (changeType = priceIncrease).

## `reportBackInStock` (type: `boolean`):

Output items that became available again (changeType = backInStock).

## `reportOutOfStock` (type: `boolean`):

Output items that sold out (changeType = outOfStock).

## `reportRemoved` (type: `boolean`):

Output items that disappeared from the store since the previous run (changeType = removed). Only stores that were read completely are checked.

## Actor input object example

```json
{
  "stores": [
    "https://www.allbirds.com",
    "https://www.gymshark.com"
  ],
  "maxProductsPerStore": 1000,
  "outputLevel": "variant",
  "includeDescription": false,
  "onlyAvailable": false,
  "monitorMode": false,
  "monitorId": "default",
  "baselineOnFirstRun": true,
  "reportNewProducts": true,
  "reportPriceDrops": true,
  "reportPriceIncreases": true,
  "reportBackInStock": true,
  "reportOutOfStock": true,
  "reportRemoved": false
}
```

# Actor output Schema

## `results` (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 = {
    "stores": [
        "https://www.allbirds.com",
        "https://www.gymshark.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mersetti/shopify-products-monitor").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": [
        "https://www.allbirds.com",
        "https://www.gymshark.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mersetti/shopify-products-monitor").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": [
    "https://www.allbirds.com",
    "https://www.gymshark.com"
  ]
}' |
apify call mersetti/shopify-products-monitor --silent --output-dataset

```

## MCP server setup

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

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/yjXJxL1NpZaYLwLxh/builds/DsO5wktWKfQNpEmLI/openapi.json
