# Shopify Price-Drop & Restock Monitor (`catalini82/shopify-price-restock-monitor`) Actor

Monitor public Shopify product feeds for price drops, price increases, restocks, out-of-stock changes, new products, and removed variants using stateful change detection.

- **URL**: https://apify.com/catalini82/shopify-price-restock-monitor.md
- **Developed by:** [Catalin Ionut Iliescu](https://apify.com/catalini82) (community)
- **Categories:** E-commerce, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 results

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/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 Price-Drop & Restock Monitor

Monitor public Shopify product feeds for useful buyer and e-commerce events: price drops, price increases, restocks, out-of-stock changes, new products, removed products, new variants, removed variants, compare-at price changes, and compact snapshot rows.

This is a stateful change monitor, not a generic Shopify catalog scraper. It is built for scheduled monitoring tasks where the valuable output is "what changed since last run?"

### What It Uses

The Actor uses only public Shopify product feed endpoints such as:

```text
https://store-domain/products.json
````

It does not log in, use browser automation, use proxies, call paid APIs, call LLMs at runtime, scrape carts or checkouts, or extract customer or personal data. Some stores disable or block this public feed. When that happens, the Actor emits a `source_error` row with `sourceHealth=error` and does not overwrite the previous state for that store.

This Actor is not affiliated with Shopify or any monitored store.

### Modes

`changes` is the default mode. It compares the current public feed with the previous saved snapshot for the same store and `stateKey`, then emits meaningful change events.

`snapshot` emits the current normalized product/variant snapshot without diffing.

`both` emits change events first, then current snapshot rows.

Snapshot rows use `eventType=initial_snapshot` and `changeReason=current snapshot row`.

### First Run Behavior

State is stored in a persistent named Apify key-value store:

```text
shopify-price-restock-monitor-state
```

State is scoped by this Actor namespace, normalized store domain, `stateKey`, and available Apify actor/user/task context. Use a unique `stateKey` for each monitoring setup or saved task.

On the first run for a given store and `stateKey`:

- If `emitInitialSnapshotAsNew=true`, current product/variant rows are emitted as `initial_snapshot` events and saved as the baseline.
- If `emitInitialSnapshotAsNew=false`, the baseline is saved and zero change events are emitted.

State stores compact normalized product and variant fields only. It does not store large raw product descriptions.

### Change Rows

The Actor emits one row per affected product/variant identity per run. `eventType` contains the highest-priority event, while boolean fields show secondary changes on the same row.

Priority order is:

```text
price_drop, restocked, price_increase, out_of_stock, compare_at_price_changed, changed, unchanged
```

For example, if a variant drops in price and restocks in the same run, `eventType` is `price_drop`, with both `isPriceDrop=true` and `isRestock=true`.

### Restock Limitations

Shopify public `products.json` feeds may expose variant `available` as true or false, but they do not guarantee exact inventory quantities. This Actor never invents inventory counts. Restock and out-of-stock events are emitted only when public availability changes from false to true or true to false. If availability is not exposed, it remains null/unknown and restock detection is not claimed.

Currency is also often not exposed by `products.json`, so `currency` may be blank.

### Input Notes

`maxItems` is a global cap across all stores in one run, not a per-store cap.

`onlyProductsMatching` and `excludeProductsMatching` are case-insensitive substring filters. They match against product title, handle, vendor, product type, tags, variant title, variant ID, product ID, and SKU before diffing and saving state.

`priceDropThresholdPercent` and `priceIncreaseThresholdPercent` default to `0`, so every detected price decrease or increase can emit a price event. Raise these values when you only want larger moves.

### Input Example

```json
{
  "stores": [
    "https://example-store.myshopify.com",
    "https://www.example-brand.com"
  ],
  "maxProductsPerStore": 250,
  "maxItems": 500,
  "mode": "changes",
  "stateKey": "default",
  "emitInitialSnapshotAsNew": true,
  "includeUnchanged": false,
  "includeFullProductData": false,
  "priceDropThresholdPercent": 0,
  "priceIncreaseThresholdPercent": 0,
  "onlyProductsMatching": [],
  "excludeProductsMatching": [],
  "requestDelayMs": 300
}
```

### Output Example

```json
{
  "source": "shopify_products_json",
  "storeUrl": "https://example-store.myshopify.com",
  "storeDomain": "example-store.myshopify.com",
  "productId": "1234567890",
  "productHandle": "classic-tee",
  "productTitle": "Classic Tee",
  "productUrl": "https://example-store.myshopify.com/products/classic-tee",
  "vendor": "Example Brand",
  "productType": "Shirts",
  "tags": ["sale"],
  "variantId": "9876543210",
  "variantTitle": "Medium / Black",
  "sku": "TEE-BLK-M",
  "currency": "",
  "oldPrice": 40.0,
  "newPrice": 32.0,
  "priceChange": -8.0,
  "priceChangePercent": -20.0,
  "oldCompareAtPrice": 50.0,
  "newCompareAtPrice": 45.0,
  "oldAvailable": false,
  "newAvailable": true,
  "eventType": "price_drop",
  "changeReason": "price decreased; compare-at price changed; availability changed from false to true",
  "isPriceDrop": true,
  "isPriceIncrease": false,
  "isRestock": true,
  "isOutOfStock": false,
  "isNewProduct": false,
  "isRemovedProduct": false,
  "isNewVariant": false,
  "isRemovedVariant": false,
  "previousHash": "abc123",
  "currentHash": "def456",
  "firstSeenAt": "2026-07-07T10:00:00Z",
  "lastSeenAt": "2026-07-07T10:05:00Z",
  "runTimestamp": "2026-07-07T10:05:00Z",
  "sourceHealth": "ok",
  "errorMessage": ""
}
```

### Public Task Ideas

- Daily price-drop monitor for a shortlist of brands.
- Restock watch for high-demand products where public availability is exposed.
- New product and new variant feed for category buyers.
- Compare-at price and sale tracking for merchandising teams.
- Competitive price movement alerts for a controlled set of public stores.

### Pricing Suggestion

Do not configure monetization until validation and marketplace positioning are final. A practical paid model later could be either `$0.50-$1.00` per monitored store run or `$2-$3` per 1,000 emitted change/snapshot records.

# Actor input Schema

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

Public Shopify store URLs or domains to monitor. The Actor uses only each store's public /products.json feed when available.

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

Maximum products to fetch and store per Shopify store. State is bounded by this value.

## `maxItems` (type: `integer`):

Maximum change or snapshot rows to write to the default dataset.

## `mode` (type: `string`):

changes compares against persistent state and emits change events. snapshot emits the current normalized feed. both emits change events plus snapshot rows.

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

State namespace for this monitoring setup. Use a unique value per saved task or monitoring workflow.

## `emitInitialSnapshotAsNew` (type: `boolean`):

When no previous state exists, emit current products and variants as initial\_snapshot rows instead of silently saving a baseline.

## `includeUnchanged` (type: `boolean`):

In changes mode, also emit unchanged product/variant rows. Usually keep this off for monitoring tasks.

## `includeFullProductData` (type: `boolean`):

Attach compact normalized product and variant data to each row. Raw descriptions are not stored or emitted.

## `priceDropThresholdPercent` (type: `number`):

Minimum percent decrease required to mark a price\_drop event. Use 0 to emit every detected decrease.

## `priceIncreaseThresholdPercent` (type: `number`):

Minimum percent increase required to mark a price\_increase event. Use 0 to emit every detected increase.

## `onlyProductsMatching` (type: `array`):

Optional terms matched against product title, handle, vendor, product type, tags, variant title, and SKU.

## `excludeProductsMatching` (type: `array`):

Optional terms used to remove matching products before diffing and saving state.

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

Delay between paginated products.json requests for the same store.

## Actor input object example

```json
{
  "stores": [
    "https://example-store.myshopify.com",
    "https://www.example-brand.com"
  ],
  "maxProductsPerStore": 250,
  "maxItems": 500,
  "mode": "changes",
  "stateKey": "default",
  "emitInitialSnapshotAsNew": true,
  "includeUnchanged": false,
  "includeFullProductData": false,
  "priceDropThresholdPercent": 0,
  "priceIncreaseThresholdPercent": 0,
  "onlyProductsMatching": [],
  "excludeProductsMatching": [],
  "requestDelayMs": 300
}
```

# Actor output Schema

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

Default dataset items with product, variant, price, availability, source-health, and stateful change metadata.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("catalini82/shopify-price-restock-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("catalini82/shopify-price-restock-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 '{}' |
apify call catalini82/shopify-price-restock-monitor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Shopify Price-Drop & Restock Monitor",
        "description": "Monitor public Shopify product feeds for price drops, price increases, restocks, out-of-stock changes, new products, and removed variants using stateful change detection.",
        "version": "0.1",
        "x-build-id": "xTJEJ1LuGmtxdIn24"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/catalini82~shopify-price-restock-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-catalini82-shopify-price-restock-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/catalini82~shopify-price-restock-monitor/runs": {
            "post": {
                "operationId": "runs-sync-catalini82-shopify-price-restock-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/catalini82~shopify-price-restock-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-catalini82-shopify-price-restock-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",
                "properties": {
                    "stores": {
                        "title": "Shopify stores",
                        "minItems": 1,
                        "maxItems": 100,
                        "type": "array",
                        "description": "Public Shopify store URLs or domains to monitor. The Actor uses only each store's public /products.json feed when available.",
                        "items": {
                            "type": "string",
                            "minLength": 1
                        },
                        "default": [
                            "https://example-store.myshopify.com",
                            "https://www.example-brand.com"
                        ]
                    },
                    "maxProductsPerStore": {
                        "title": "Max products per store",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Maximum products to fetch and store per Shopify store. State is bounded by this value.",
                        "default": 250
                    },
                    "maxItems": {
                        "title": "Max output items",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Maximum change or snapshot rows to write to the default dataset.",
                        "default": 500
                    },
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "changes",
                            "snapshot",
                            "both"
                        ],
                        "type": "string",
                        "description": "changes compares against persistent state and emits change events. snapshot emits the current normalized feed. both emits change events plus snapshot rows.",
                        "default": "changes"
                    },
                    "stateKey": {
                        "title": "State key",
                        "minLength": 1,
                        "maxLength": 80,
                        "type": "string",
                        "description": "State namespace for this monitoring setup. Use a unique value per saved task or monitoring workflow.",
                        "default": "default"
                    },
                    "emitInitialSnapshotAsNew": {
                        "title": "Emit initial snapshot as new",
                        "type": "boolean",
                        "description": "When no previous state exists, emit current products and variants as initial_snapshot rows instead of silently saving a baseline.",
                        "default": true
                    },
                    "includeUnchanged": {
                        "title": "Include unchanged",
                        "type": "boolean",
                        "description": "In changes mode, also emit unchanged product/variant rows. Usually keep this off for monitoring tasks.",
                        "default": false
                    },
                    "includeFullProductData": {
                        "title": "Include full normalized product data",
                        "type": "boolean",
                        "description": "Attach compact normalized product and variant data to each row. Raw descriptions are not stored or emitted.",
                        "default": false
                    },
                    "priceDropThresholdPercent": {
                        "title": "Price drop threshold percent",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "number",
                        "description": "Minimum percent decrease required to mark a price_drop event. Use 0 to emit every detected decrease.",
                        "default": 0
                    },
                    "priceIncreaseThresholdPercent": {
                        "title": "Price increase threshold percent",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "number",
                        "description": "Minimum percent increase required to mark a price_increase event. Use 0 to emit every detected increase.",
                        "default": 0
                    },
                    "onlyProductsMatching": {
                        "title": "Only products matching",
                        "maxItems": 100,
                        "type": "array",
                        "description": "Optional terms matched against product title, handle, vendor, product type, tags, variant title, and SKU.",
                        "items": {
                            "type": "string",
                            "minLength": 1
                        },
                        "default": []
                    },
                    "excludeProductsMatching": {
                        "title": "Exclude products matching",
                        "maxItems": 100,
                        "type": "array",
                        "description": "Optional terms used to remove matching products before diffing and saving state.",
                        "items": {
                            "type": "string",
                            "minLength": 1
                        },
                        "default": []
                    },
                    "requestDelayMs": {
                        "title": "Request delay ms",
                        "minimum": 0,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Delay between paginated products.json requests for the same store.",
                        "default": 300
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
