# Shopify Store Monitor: Price Drop, Stock & New Product Alerts (`fetchwise/shopify-store-monitor`) Actor

Watch any Shopify store or collection for new and removed products, price drops, restocks and sell-outs, with Slack, Discord or webhook alerts. Can also export the full product catalog. Fast, no browser, no proxy.

- **URL**: https://apify.com/fetchwise/shopify-store-monitor.md
- **Developed by:** [kelvin lab](https://apify.com/fetchwise) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 store checks

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 Monitor: Price Drop, Stock & New Product Alerts

Track any Shopify store, or just one of its collections, and get told what changed since the last check: **new products, removed products, new variants, price drops, price increases, sell-outs and restocks**. Alerts go straight to **Slack or Discord**, or to any webhook (Make, n8n, Zapier).

It reads each store's public Shopify product feed: **no browser, no login, and no proxy needed** for most stores. A 700-product store is checked in about 3 seconds.

### What you can use it for

- **Competitor price tracking:** see every price drop and price increase across your competitors' Shopify stores, with old price, new price and % change.
- **Restock and sell-out alerts:** know when a size, color or product comes back in stock or sells out.
- **New product drops:** get notified the moment a store adds a product or a new variant.
- **Shopify catalog export:** download every product variant of a store with price, compare-at price, SKU and stock status (full catalog mode).

### Why this monitor

- **Only real changes.** If a store briefly returns an empty catalog, you don't get hundreds of "removed" and then "new" alerts. When only part of a catalog is fetched, nothing is wrongly flagged as removed or new.
- **No lost alerts.** If your Slack, Discord or webhook endpoint is down, the alert is retried on the next run.
- **Ready-to-read messages.** Slack and Discord get one clean message per store, for example `🔻 Price drop: Men's Tree Runner / 10: $98.00 → $78.00 (-20.41%)`.
- **Collections and keywords.** Watch only `/collections/mens`, or only products matching "runner" and not "kids".
- **Store currency** is included in every row.
- **You only pay for what you use.** Failed stores are free, and the run stops cleanly at your maximum cost per run. Changes that didn't fit in the budget are reported on the next run.

### How it works

1. The **first run** saves a baseline of every product and variant for each store.
2. **Every later run** compares the store with the last run and outputs only what changed.
3. Optionally, it **sends alerts** to Slack, Discord or any webhook.

Schedule it hourly or daily with Apify Schedules and you have a competitor monitor on autopilot.

### Quick start

1. Add one or more store URLs, e.g. `https://www.allbirds.com` or `gymshark.com`.
2. Run once. This saves the baseline, and the dataset shows one `STORE_SUMMARY` row per store.
3. Create a **Schedule** (Console → Schedules → Create) with the same input, daily or hourly.
4. Optional: paste a Slack or Discord webhook URL into **Alert webhook URL**.

### Input

| Field | Description |
|---|---|
| `storeUrls` | Stores to monitor. A domain or any store URL works. A collection URL (`/collections/<name>`) monitors just that collection. Repeated URLs are checked once. |
| `outputMode` | `changes` (default) outputs only changes. `all` also outputs every variant as a `SNAPSHOT` row (full catalog export). |
| `trackEvents` | Which change types to report. Default: all. |
| `minPriceChangePercent` | Ignore price moves smaller than this %. Measured from the last reported price, so slow drifts are still caught. |
| `webhookUrl` | Slack or Discord webhook (formatted message), or any URL (JSON). Stored encrypted. |
| `keywords` / `excludeKeywords` | Monitor only products whose title, tags, vendor or type match, or skip those that do. |
| `maxProductsPerStore` | Cap products fetched per store. `0` = no limit. With a cap, removed products are not reported. |
| `stateKey` | Monitor name. Runs with the same name share history. Changing keywords or the cap starts a fresh baseline automatically. |
| `proxyConfiguration` | Optional. Apify datacenter proxy or your own proxies, if a store rate-limits you. Residential proxy is not supported. |

```json
{
  "storeUrls": ["https://www.allbirds.com", "kith.com/collections/mens-footwear"],
  "minPriceChangePercent": 5,
  "keywords": ["runner"],
  "webhookUrl": "https://hooks.slack.com/services/..."
}
```

### Output

One row per change:

```json
{
  "changeType": "PRICE_DROP",
  "store": "https://www.allbirds.com",
  "storeName": "Allbirds",
  "productTitle": "Men's Tree Runners",
  "variantTitle": "10 / Black",
  "sku": "TR-M-10-BLK",
  "oldPrice": 98,
  "newPrice": 78,
  "priceChangePercent": -20.41,
  "currency": "USD",
  "available": true,
  "productUrl": "https://www.allbirds.com/products/mens-tree-runners",
  "imageUrl": "https://cdn.shopify.com/...",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "detectedAt": "2026-10-18T09:00:00.000Z"
}
```

Change types: `NEW_PRODUCT`, `REMOVED_PRODUCT`, `NEW_VARIANT`, `PRICE_DROP`, `PRICE_INCREASE`, `OUT_OF_STOCK`, `BACK_IN_STOCK`, plus `SNAPSHOT` rows in full catalog mode.

Each store also gets one free **`STORE_SUMMARY`** row per run:

```json
{
  "changeType": "STORE_SUMMARY",
  "store": "https://www.allbirds.com",
  "storeName": "Allbirds",
  "status": "ok",
  "productsMonitored": 692,
  "variants": 7454,
  "changesDelivered": 3,
  "changesPostponed": 0,
  "scanComplete": true,
  "notification": "sent",
  "pendingNotifications": 0
}
```

`status` is one of:

- `baseline_saved`: first run for this store.
- `ok`: checked; `changesDelivered` says how many changes were found.
- `error`: the store could not be checked, with the reason in `error`, e.g. `NOT_FOUND` (not a Shopify store), `BLOCKED` (password-protected), `DOMAIN_NOT_FOUND` or `EMPTY_CATALOG`.
- `skipped_budget`: your maximum cost per run was reached.
- `skipped_newer_state`: a newer run of the same monitor already checked this store.

The dataset has three views: **Changes**, **Store summary** and **Full catalog**. Export them as JSON, CSV or Excel, or read them through the Apify API.

### Full catalog export

Set `outputMode` to `all` to also get every variant of every monitored product as a `SNAPSHOT` row: product and variant title, SKU, price, compare-at price, currency, stock status, vendor, type, tags, image and product URL. It works on the first run too, so it doubles as a **Shopify product scraper**.

Snapshot rows are charged per row, and each variant is one row. For example, Allbirds has 692 products in 7,454 variants (sizes), so a full export costs 7,454 × $0.0002 ≈ $1.49.

### Alerts

- **Slack:** create an [Incoming Webhook](https://api.slack.com/messaging/webhooks) and paste its URL.
- **Discord:** Channel settings → Integrations → Webhooks → New Webhook → Copy URL. Mentions such as `@everyone` in product titles never ping anyone.
- **Make, n8n, Zapier or your API:** use their "custom webhook" URL. You receive `{ text, store, storeName, totalChanges, changes[] }`, one request per store with changes (up to 200 changes per request; the full list is in the dataset).

Long Slack and Discord messages are trimmed to the platform's limit and end with "…and N more" plus a link to the full dataset.

### Pricing

Pay per event: you pay only for what the Actor does.

| Event | Price | When |
|---|---|---|
| Store check | **$0.004** | Per store or collection checked, per started block of 5,000 products. A 12,000-product store counts as 3 checks. |
| Change detected | **$0.001** | Per change row delivered. |
| Catalog item | **$0.0002** | Per `SNAPSHOT` row (one per variant), in full catalog mode only. |

Apify also charges its standard Actor start fee ($0.00005 per run). Summary rows are free, and stores that fail (not Shopify, password-protected, unreachable) are not charged.

**Examples:**

- 1 store (under 5,000 products) checked daily: about **$0.12/month** plus $0.001 per change.
- 20 competitor stores (about 2,000 products each) checked daily with about 50 changes a day: about **$3.90/month**.
- Full catalog export of a 2,000-product store with 10,000 variants: about **$2.00**.

Set a **maximum cost per run** in the run options to stay in control. When it is reached, the run stops cleanly and any changes that didn't fit are reported on the next run.

### Limitations

- Works on stores that expose the standard Shopify product feed (`/products.json`), which is the vast majority. Password-protected stores and stores that disable the feed or sit behind bot protection are reported as `error`.
- Shopify lists at most 25,000 products per store or collection. For bigger stores the first 25,000 are monitored and removed products are not reported; monitor collections separately to cover more.
- Prices are in the store's own currency and are not converted.
- Shopify only shows published products, so hidden or draft products are not tracked.

### FAQ

**Why are there no changes on the first run?**
The first run creates the baseline. Changes are reported from the second run on.

**How often should I run it?**
Daily is enough for most price and catalog tracking. For restocks and product drops, run it hourly; each run is charged per store check, so 24 checks of one store cost about $0.10 a day.

**Can I track hundreds of stores?**
Yes. Add them all to `storeUrls`. Each store and collection keeps its own history.

**Can I export all products of a Shopify store?**
Yes. Use `outputMode: all` and export the **Full catalog** view as CSV, Excel or JSON.

**Can two schedules run at the same time?**
Yes. If two runs of the same monitor overlap, the newer observation wins and the older run doesn't overwrite it.

**I changed keywords and got no changes on the next run. Why?**
A different filter means a different set of products, so the Actor starts a fresh baseline instead of reporting false changes.

**Is this legal?**
The Actor reads only public product data that any visitor's browser receives. It doesn't log in or access private data. Respect each store's terms of service.

### Support

Found a bug or need a field? Open an issue on the Actor's **Issues** tab. We usually reply within 48 hours.

# Actor input Schema

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

Shopify stores to monitor. A domain or any store URL works, e.g. https://www.allbirds.com or gymshark.com. To watch only one collection, use its URL, e.g. https://www.allbirds.com/collections/mens.

## `outputMode` (type: `string`):

"changes" outputs only what changed since the last run (best for scheduled monitoring). "all" also outputs every product variant as a SNAPSHOT row (full catalog export, charged per row).

## `trackEvents` (type: `array`):

Which change types to report.

## `minPriceChangePercent` (type: `integer`):

Ignore price changes smaller than this percentage, measured from the last reported price (so slow drifts are still caught). 0 reports every change.

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

Optional. Slack and Discord webhook URLs get a ready-to-read message per store. Any other URL receives JSON (works with Make, n8n, Zapier). Alerts that fail to send are retried on the next run.

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

Optional. Monitor only products whose title, tags, vendor or product type contain any of these words (case-insensitive).

## `excludeKeywords` (type: `array`):

Optional. Ignore products whose title, tags, vendor or product type contain any of these words.

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

Stop after fetching this many products per store. 0 = no limit (Shopify lists at most 25,000 products per store or collection). With a limit, removed products are not reported (the Actor can't tell removed from not fetched).

## `stateKey` (type: `string`):

Runs with the same monitor name share history. Use different names to keep separate watchlists. Changing keywords or the product limit starts a fresh baseline automatically.

## `proxyConfiguration` (type: `object`):

Most Shopify stores work without a proxy. If a store rate-limits you, enable Apify datacenter proxy or add your own proxy URLs. Residential proxy is not supported.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com"
  ],
  "outputMode": "changes",
  "trackEvents": [
    "NEW_PRODUCT",
    "REMOVED_PRODUCT",
    "NEW_VARIANT",
    "PRICE_DROP",
    "PRICE_INCREASE",
    "OUT_OF_STOCK",
    "BACK_IN_STOCK"
  ],
  "minPriceChangePercent": 0,
  "maxProductsPerStore": 0,
  "stateKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per change since the last run: new or removed products and variants, price drops and increases, sell-outs and restocks.

## `storeSummary` (type: `string`):

One STORE_SUMMARY row per store: status, products monitored, changes delivered and alert status.

## `catalog` (type: `string`):

Every product variant as a SNAPSHOT row with price, SKU and stock. Filled only when Output mode is "all".

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

Status of every store checked in this run.

# 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"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchwise/shopify-store-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://www.allbirds.com"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("fetchwise/shopify-store-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 '{
  "storeUrls": [
    "https://www.allbirds.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call fetchwise/shopify-store-monitor --silent --output-dataset

```

## MCP server setup

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