# Shopify Competitor Price Stock Monitor & Catalog Scraper (`leafy-dev-jr/shopify-competitor-price-stock-monitor`) Actor

Detect price drops, sales, restocks, sold-outs, and new product launches across competitor Shopify stores.

- **URL**: https://apify.com/leafy-dev-jr/shopify-competitor-price-stock-monitor.md
- **Developed by:** [Leafy](https://apify.com/leafy-dev-jr) (community)
- **Categories:** Automation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Shopify Competitor Price & Stock Monitor

**Detect price drops, sales, restocks, sold-outs, and new product launches across competitor Shopify stores, automatically.**

Paste your competitors' Shopify URLs and press **Start**. The first run saves a baseline of their catalog. Every run after that reports exactly what changed, so you know the moment a rival cuts a price, starts a sale, restocks a sold-out size, or launches something new.

---

### What it detects

| Event | Meaning |
| --- | --- |
| `PRICE_DROP` / `PRICE_INCREASE` | A variant got cheaper or more expensive |
| `SALE_STARTED` / `SALE_ENDED` | A compare-at ("was") price appeared or disappeared |
| `OUT_OF_STOCK` / `RESTOCKED` | A variant sold out or came back |
| `NEW_PRODUCT` / `REMOVED_PRODUCT` | A product was added, removed, or unpublished |
| `NEW_VARIANT` / `REMOVED_VARIANT` | A size, color, or option was added or removed |
| `TITLE_CHANGED` / `VENDOR_CHANGED` / `TAG_CHANGED` | Metadata edits (off by default) |

Every change gets a plain-English **summary** like *"Price dropped 20% from 50.00 GBP to 40.00 GBP on Vital Seamless Leggings (Black / M)"*.

---

### Quick start

1. Paste one or more store URLs into **Competitor Shopify store URLs** (the homepage is fine).
2. Press **Start**.
3. *(Optional)* Add **keywords** to watch only certain products, or a **webhook** to get alerts.

**First run = baseline.** There is nothing to compare against yet, so the run reports **0 changes** and clearly says a baseline was created. That is expected. Change events start on the second run.

**Schedule it** (Apify > **Schedule Actor**) to monitor on autopilot: **hourly** for sales and restocks, **daily** for pricing, **weekly** for new launches.

---

### Output

The dataset holds your **change events**, plus **one summary row per store** with a plain-English status (baseline created, number of changes, or an error). The **full catalog** for each store is saved separately in the snapshot Key-Value store, so the dataset stays focused on what actually changed.

| `recordType` | What it is |
| --- | --- |
| `change_event` | One detected change, with severity and summary |
| `store_summary` | Per-store status message, totals, and change counts |

Example change event:

```json
{
  "recordType": "change_event",
  "eventType": "PRICE_DROP",
  "storeName": "Gymshark",
  "productTitle": "Vital Seamless Leggings",
  "variantTitle": "Black / M",
  "sku": "GS-VSL-BLK-M",
  "oldPrice": 50,
  "newPrice": 40,
  "priceChangePercent": -20,
  "currency": "GBP",
  "severity": "high",
  "changeSummary": "Price dropped 20.0% from 50.00 GBP to 40.00 GBP on Vital Seamless Leggings (Black / M)",
  "detectedAt": "2026-07-04T09:00:00.000Z",
  "productUrl": "https://gymshark.com/products/vital-seamless-leggings"
}
````

If a store cannot be read (invalid URL, blocked, or not Shopify), you still get a `store_summary` row with an `errors` message, and the run keeps going for every other store.

Want the whole product list, not just the changes? See **Get the full catalog as a file** below.

***

### Get the full catalog as a file

Sometimes you want a competitor's entire catalog, not just what changed. Turn on **Export the full catalog as files** in the input and choose one or more formats:

- **Excel** (`catalog.xlsx`) opens straight in Excel or Google Sheets.
- **JSON** (`catalog.json`) is a clean array, one row per variant.
- **CSV** (`catalog.csv`) opens in any spreadsheet.

**Where to get it:** open the run, go to the **Storage** tab (or the **Output** tab), and download `catalog.xlsx`, `catalog.json`, or `catalog.csv`.

Each row has store, product, variant, SKU, price, sale price, availability, currency, vendor, type, tags, image URL, and product URL. This runs on **every run, including the very first one**, so you can grab a full catalog immediately even before any changes exist.

Prefer the catalog as dataset rows instead (for a one-off export)? Set `outputMode` to `both` or `fullSnapshot` in the JSON input tab.

***

### How it remembers changes

To know what changed, the actor remembers what each store looked like last time:

- After every run it saves a **snapshot** (a JSON photo of the catalog) to an Apify **Key-Value store** named `shopify-monitor`, with **one record per store**, keyed by domain (for example `gymshark-com`). This is also where you can browse a store's full current catalog.
- The next run loads that record and compares it to the live catalog. That comparison is where change events come from, and why the first run finds nothing.
- Find it in **Storage > Key-value stores > `shopify-monitor`**. It stores only public catalog data and keeps just the latest snapshot per store, so scheduled runs always compare against the previous run automatically.

Need to start a store over? Add its URL to **Reset baseline for these stores** and the next run rebuilds its baseline from scratch. Other stores keep their history.

***

### Options

The form shows **store URLs**, **keywords**, **webhook**, **export the full catalog as files**, and **reset baseline for these stores**. Everything below is optional, has a sensible default, and can be set from the **JSON** input tab.

| Field | Default | What it does |
| --- | --- | --- |
| `storeUrls` *(required)* | | Competitor Shopify store URLs |
| `keywords` | `[]` | Only track products matching any keyword |
| `webhookUrl` | | POST a change summary here (Slack, Zapier, Make) |
| `catalogExportFormats` | `[]` | Save the full catalog as files in storage: any of `json`, `xlsx`, `csv` |
| `resetBaselineStores` | `[]` | Forget saved history for these stores and start them fresh |
| `outputMode` | `changesOnly` | `changesOnly`, `both`, or `fullSnapshot` (also writes catalog rows to the dataset) |
| `collectionHandles` | `[]` | Track specific collections instead of the whole catalog |
| `minPriceChangePercent` | `0` | Ignore price moves smaller than this % |
| `includeNewProducts` / `includeRemovedProducts` | `true` | Toggle new and removed product events |
| `includeStockChanges` / `includeSaleChanges` | `true` | Toggle restock/sold-out and sale events |
| `includeVariantChanges` / `includeVariants` | `true` | Toggle variant tracking |
| `includeMetadataChanges` | `false` | Title, vendor, and tag events (noisy) |
| `maxProductsPerStore` | `5000` | Safety cap per store |
| `maxConcurrency` | `3` | Stores fetched in parallel |
| `requestDelayMs` | `300` | Delay between requests to a store |
| `resetBaseline` | `false` | Reset the baseline for every store at once |
| `storeSnapshotKeyPrefix` | `shopify-monitor` | Snapshot store name (change it to run separate setups) |

**Webhook payload:** when changes are found, the actor POSTs `{ totalChanges, countsByEventType, topChanges (up to 20), runUrl }`. A failed webhook never fails the run.

***

### Pricing

This actor uses **pay per event**: you are charged one small flat fee for each competitor **store checked in a run**, no matter how big its catalog is. A store with 50,000 products costs the same as one with 50.

- Watching 5 competitors on a daily schedule is about 150 store checks per month.
- Stores that error out (blocked, not Shopify, or empty) are **not** charged.
- There is **no per-product or per-result fee**, so a large catalog never inflates your bill.
- Your run also has a max-cost limit you can set in Apify, so spend is always capped.

***

### Good to know

- **Availability, not exact stock counts.** Shopify's public data exposes an in-stock or out-of-stock flag, not quantities.
- **Public data only.** No login, Admin API, tokens, or customer and order data. Just the public product JSON.
- **Some stores block it.** A minority disable `/products.json` or sit behind a bot wall. Those return an error row instead of crashing the run.
- **Currency is best-effort** (read from the storefront when possible, and may be `null`).
- **Polite by default:** low concurrency, request delays, and automatic retry with backoff on 429 and 5xx.
- Detects and reports changes. It does not reprice your own store.

# Actor input Schema

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

Paste one or more competitor store URLs (the homepage is fine) and press Start. That is the only thing you need.

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

Leave empty to track everything. Example: hoodie, leggings.

## `webhookUrl` (type: `string`):

Optional. When changes are found, a summary is sent here. Paste a Slack, Zapier, or Make webhook. If it fails, your run still succeeds.

## `catalogExportFormats` (type: `array`):

Optional. Also save every monitored product and variant as downloadable file(s) in this run's storage. Pick JSON, Excel (xlsx), and/or CSV. Leave empty to skip. (The dataset still holds only the changes.)

## `resetBaselineStores` (type: `array`):

Optional. Paste a store URL here to forget its saved history and start fresh on this run, as if it were brand new. Handy after a store changes a lot, or if its saved data looks wrong. All other stores keep their history.

## Actor input object example

```json
{
  "storeUrls": [
    "https://store1.com",
    "https://store2.myshopify.com"
  ],
  "keywords": [],
  "catalogExportFormats": [],
  "resetBaselineStores": []
}
```

# Actor output Schema

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

Change events since the previous run: price drops and increases, sales starting and ending, restocks, sold-outs, and new or removed products and variants. Empty on the first (baseline) run.

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

One entry per store with a plain-English status message (baseline created, number of changes, or an error), plus counts and the location of the full catalog snapshot.

## `catalogExcel` (type: `string`):

Every monitored product and variant as an .xlsx spreadsheet. Only produced when 'xlsx' is chosen in 'Export the full catalog as files'.

## `catalogJson` (type: `string`):

The same full catalog as a JSON file. Only produced when 'json' is chosen in 'Export the full catalog as files'.

## `catalogCsv` (type: `string`):

The same full catalog as a CSV file for Excel or Google Sheets. Only produced when 'csv' is chosen in 'Export the full catalog as files'.

# 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://gymshark.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leafy-dev-jr/shopify-competitor-price-stock-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 = { "storeUrls": ["https://gymshark.com"] }

# Run the Actor and wait for it to finish
run = client.actor("leafy-dev-jr/shopify-competitor-price-stock-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://gymshark.com"
  ]
}' |
apify call leafy-dev-jr/shopify-competitor-price-stock-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=leafy-dev-jr/shopify-competitor-price-stock-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Shopify Competitor Price Stock Monitor & Catalog Scraper",
        "description": "Detect price drops, sales, restocks, sold-outs, and new product launches across competitor Shopify stores.",
        "version": "1.0",
        "x-build-id": "9eRdfrDRYfDDOSV8I"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/leafy-dev-jr~shopify-competitor-price-stock-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-leafy-dev-jr-shopify-competitor-price-stock-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/leafy-dev-jr~shopify-competitor-price-stock-monitor/runs": {
            "post": {
                "operationId": "runs-sync-leafy-dev-jr-shopify-competitor-price-stock-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/leafy-dev-jr~shopify-competitor-price-stock-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-leafy-dev-jr-shopify-competitor-price-stock-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "storeUrls"
                ],
                "properties": {
                    "storeUrls": {
                        "title": "Competitor Shopify store URLs",
                        "type": "array",
                        "description": "Paste one or more competitor store URLs (the homepage is fine) and press Start. That is the only thing you need.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywords": {
                        "title": "Only track products matching these words (optional)",
                        "type": "array",
                        "description": "Leave empty to track everything. Example: hoodie, leggings.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "webhookUrl": {
                        "title": "Get notified (webhook URL)",
                        "type": "string",
                        "description": "Optional. When changes are found, a summary is sent here. Paste a Slack, Zapier, or Make webhook. If it fails, your run still succeeds."
                    },
                    "catalogExportFormats": {
                        "title": "Export the full catalog as files",
                        "type": "array",
                        "description": "Optional. Also save every monitored product and variant as downloadable file(s) in this run's storage. Pick JSON, Excel (xlsx), and/or CSV. Leave empty to skip. (The dataset still holds only the changes.)",
                        "items": {
                            "type": "string",
                            "enum": [
                                "json",
                                "xlsx",
                                "csv"
                            ],
                            "enumTitles": [
                                "JSON",
                                "Excel (.xlsx)",
                                "CSV"
                            ]
                        },
                        "default": []
                    },
                    "resetBaselineStores": {
                        "title": "Reset baseline for these stores",
                        "type": "array",
                        "description": "Optional. Paste a store URL here to forget its saved history and start fresh on this run, as if it were brand new. Handy after a store changes a lot, or if its saved data looks wrong. All other stores keep their history.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
