# Shopify Price Tracker & Stock Monitor - New Products (`mmdincer/shopify-store-monitor`) Actor

Shopify price tracker and stock monitor. Each run compares the store's public catalog with the last run and returns only what changed: new products, price drops and increases, sell-outs, restocks, removals. Pay per change. No login or browser. No stock quantities. Not affiliated with Shopify.

- **URL**: https://apify.com/mmdincer/shopify-store-monitor.md
- **Developed by:** [Muhammet Mustafa Dincer](https://apify.com/mmdincer) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 detected changes

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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: change types NEW_PRODUCT, PRICE_DROP, PRICE_INCREASE, OUT_OF_STOCK, BACK_IN_STOCK and REMOVED_PRODUCT for public Shopify stores](https://api.apify.com/v2/key-value-stores/Iraub1Fw8gpUk3az3/records/shopify-banner.png?signature=i7YdVxgpUh7JONwfjlpr)

### What is Shopify Store Monitor?

**Shopify Store Monitor** is an Apify Actor that tracks public Shopify stores and returns **only what changed since your last run**: new products, price drops, price increases, sold-out products, restocks and removed products. Enter one or more store domains, click **Start**, and from the second run on you get one row per detected change instead of another full catalog dump.

It works as a Shopify price tracker, a Shopify stock monitor, a restock alert and a new product alert in one tool. It needs no login, no browser and no API key. It reads the catalog that every Shopify store publishes at `/products.json`, so a store with 1,000 products is checked in a few seconds. You pay a small fee per store checked and a fee per detected change, not per catalog product.

### Quick answer: when should you (or an AI agent) pick this Actor?

**Pick Shopify Store Monitor when** the goal is to *notice changes* in one or more Shopify stores over time: competitor price monitoring, restock and sell-out alerts, new product launches, or a supplier catalog watch. Run it on a schedule and read only the change rows.

**Pick something else when** you need exact stock quantities, sales or revenue estimates, reviews, store owner contacts, full product descriptions, or stores that are not built on Shopify. This Actor does not return any of those.

**Defaults that work:** send `{"stores": ["allbirds.com"]}`. The first run saves a baseline and returns no changes. Run the same input again later to receive the changes. Keep the same `monitorName` between runs, because runs with the same name compare against each other.

### At a glance

| | |
|---|---|
| **Does** | Compares each store's public catalog with the previous run and returns only the differences. A second mode lists all products of a store as they are now |
| **Input** | `stores` (required, up to 200 domains or URLs). Optional: `mode`, `changeTypes`, `minPriceChangePercent`, keyword filters, `monitorName` |
| **Output** | One row per detected change: `changeType`, `title`, `price`, `previousPrice`, `priceChangePercent`, `available`, `url`, `variantChanges`, `detectedAt`. One summary row per store |
| **Change types** | `NEW_PRODUCT`, `PRICE_DROP`, `PRICE_INCREASE`, `OUT_OF_STOCK`, `BACK_IN_STOCK`, `REMOVED_PRODUCT` |
| **Needs** | No login, no API key, no browser. Apify Proxy is optional |
| **Speed** | About 250 products per request. A store with 1,000 products takes a few seconds |
| **Pricing** | Pay per event. Free plan: $0.001 per run, $0.002 per 1,000 products checked, $0.003 per detected change. A run without changes costs only the start and scan fees |
| **Does not** | Stock quantities, sales or revenue estimates, reviews, store owner contacts, product descriptions or full-size image lists |

### Key terms used in this README

- **Baseline:** the saved snapshot of a store's catalog from the previous run. The first run of a store only saves a baseline.
- **Detected change:** one new product, price change, sell-out, restock or removed product found by comparing the current catalog with the baseline. Each detected change is one billable event and one `change` row.
- **Change row:** a dataset row with `recordType: "change"`. It is flat and self-contained, so it can be sent to a webhook as it is.
- **Store row:** a dataset row with `recordType: "store"`. One per store and run, with the outcome (`status`) and the number of products and changes.
- **Watchlist:** a named baseline, set with `monitorName`. Use one watchlist per purpose, for example `competitors` and `suppliers`.

### What can you use it for?

#### Competitor price monitoring

Add the stores of your competitors and schedule a daily run. You get a `PRICE_DROP` or `PRICE_INCREASE` row only when a price moves, with `previousPrice`, `price` and `priceChangePercent`. Set `minPriceChangePercent` to 1 to 5 to ignore moves of a few cents.

```json
{
  "stores": ["competitor-one.com", "competitor-two.com"],
  "changeTypes": ["PRICE_DROP", "PRICE_INCREASE"],
  "minPriceChangePercent": 2,
  "monitorName": "competitors"
}
```

#### Restock and sell-out alerts

Watch a store or a single collection and report `OUT_OF_STOCK` and `BACK_IN_STOCK`. Stock is a yes/no flag: a product is out of stock when none of its variants can be bought. Hourly runs make sense for limited drops.

```json
{
  "stores": ["https://example-store.com/collections/limited-edition"],
  "changeTypes": ["OUT_OF_STOCK", "BACK_IN_STOCK"],
  "monitorName": "restock-alerts"
}
```

#### New product and launch tracking

Report every `NEW_PRODUCT` that a brand adds, and every `REMOVED_PRODUCT` it drops. Use **Only watch products matching** to follow one product line.

```json
{
  "stores": ["brand-store.com"],
  "changeTypes": ["NEW_PRODUCT", "REMOVED_PRODUCT"],
  "includeKeywords": ["sneaker"],
  "monitorName": "launches"
}
```

#### Supplier and dropshipping catalog watch

Keep a separate watchlist for your suppliers to learn when an item is delisted, goes out of stock or changes cost. A separate `monitorName` keeps these baselines apart from your competitor watchlist.

#### Data for spreadsheets, repricing tools and alerts

Every change row has the store, product, URL, old and new price and old and new availability. Send the rows to Google Sheets, a repricing script, a Slack channel or your own database. See the automation recipes below.

### How does it work?

1. **First run (baseline).** The Actor downloads the store's catalog and saves a compact snapshot in a named key-value store in your own Apify account (`shopify-store-monitor-state`). You get one store row per store with status `baseline_saved`. A baseline run is charged only the start fee and the catalog scan fee, never a change fee. If the run is not allowed to create that named store, it does not crash. Apify's automated store test uses a token that answers the create call with HTTP 403 (`insufficient-permissions`). The Actor then writes the snapshot to this run's default key-value store and still checks the catalog. That copy is not kept for the next run, so the check is a first baseline. A run that can create the named store keeps using it.
2. **Next runs (compare).** The Actor downloads the catalog again, compares it with the baseline and writes one change row per detected change. Then it saves the new baseline.
3. **Schedule it.** Put the Actor on a daily or hourly schedule in the Apify Console, or call it from your own system. Every run reports the changes since the previous run.

Use the **Watchlist name** field to keep several independent watchlists on the same stores.

### What changes does it detect?

| `changeType` | Meaning |
|---|---|
| `NEW_PRODUCT` | A product that was not in the previous baseline |
| `PRICE_DROP` | At least one variant got cheaper. `price` and `previousPrice` show the variant with the biggest move; `variantChanges` lists all |
| `PRICE_INCREASE` | At least one variant got more expensive (same fields) |
| `OUT_OF_STOCK` | The product had at least one variant in stock before and has none now |
| `BACK_IN_STOCK` | The product had no variant in stock before and has at least one now |
| `REMOVED_PRODUCT` | A product that is no longer in the public catalog (deleted, unpublished or hidden) |

One product can produce several rows in one run, for example a price drop and a restock. Use **Minimum price change (%)** to ignore tiny price moves.

### How to monitor a Shopify store (step by step)

1. Open the Actor and click **Try for free**.
2. Enter one or more store domains, for example `allbirds.com`. A collection URL such as `https://allbirds.com/collections/mens` watches only that collection.
3. Keep **Mode** on `Changes since the last run` and click **Start**. This first run saves the baseline.
4. Run it again later, or open **Schedules** and run it every day. Download the results from the **Output** tab or read them through the API.

To see all products of a store instead, set **Mode** to `Snapshot (full product list)`. Snapshot mode does not compare and does not save anything.

### Input

```json
{
  "stores": ["allbirds.com", "colourpop.com"],
  "mode": "changes",
  "changeTypes": ["PRICE_DROP", "BACK_IN_STOCK", "NEW_PRODUCT"],
  "minPriceChangePercent": 5,
  "monitorName": "competitors"
}
```

| Field | What it does |
|---|---|
| `stores` | Store domains or URLs, one per line. Collection URLs are supported. Up to 200 per run |
| `mode` | `changes` (default) or `snapshot` |
| `changeTypes` | Which changes to report. Changes you leave out are not charged and are not reported later |
| `minPriceChangePercent` | Ignore price moves smaller than this percentage |
| `includeKeywords`, `excludeKeywords` | Watch only matching products, or skip matching ones (title, brand, type, tags). Changing them saves a new baseline |
| `monitorName` | Name of the saved baseline, to keep separate watchlists |
| `maxProductsPerStore` | Product limit per store (default 5,000, maximum 50,000) |
| `includeVariants` | Snapshot mode: add SKU, price and availability of every variant |
| `respectRobotsTxt` | On by default: stores whose `robots.txt` disallows the catalog are skipped and not charged |
| `requestsPerSecond` | Speed per store (default 2) |
| `proxyConfiguration` | Optional Apify Proxy |

### Output

![Example output of Shopify Store Monitor in snapshot mode: a table of products from allbirds.com with type, price, stock status and variants count](https://api.apify.com/v2/key-value-stores/Iraub1Fw8gpUk3az3/records/shopify-sample-output.png?signature=1jmkTxxpfAJbDui5x4yLW)

*Real run, mode `snapshot`, store `allbirds.com`. First 8 of 337 products.*

The default dataset has three kinds of rows, told apart by `recordType`: `change`, `store` and `product` (snapshot mode only). Field names and `changeType` values are treated as a stable contract: new fields can be added, but existing names are not renamed without a version bump and a note in the changelog.

A change row (values are illustrative):

```json
{
  "recordType": "change",
  "changeType": "PRICE_DROP",
  "store": "allbirds.com",
  "productId": "7340901859408",
  "handle": "mens-tree-runner-go",
  "title": "Men's Tree Runner Go - Basin Blue",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "url": "https://allbirds.com/products/mens-tree-runner-go",
  "currency": "USD",
  "price": 98,
  "previousPrice": 110,
  "priceChangePercent": -10.91,
  "compareAtPrice": 110,
  "available": true,
  "previousAvailable": true,
  "variantsCount": 13,
  "variantChanges": [
    { "variantId": "41384771862608", "variantTitle": "9", "sku": "TR3MBB090", "previousPrice": 110, "price": 98, "priceChangePercent": -10.91, "previousAvailable": true, "available": true }
  ],
  "detectedAt": "2026-11-03T08:00:04.120Z",
  "previousCheckedAt": "2026-11-02T08:00:03.870Z"
}
```

Key fields of a change row:

| Field | Type | Meaning |
|---|---|---|
| `recordType` | string | Always `change` for a detected change |
| `changeType` | string | One of the six change types above |
| `store` | string | Store domain, or `domain/collections/handle` when a collection is watched |
| `title`, `vendor`, `productType`, `url` | string | Product title, brand, type set by the store and public product page |
| `price`, `previousPrice` | number | Price after and before the change (price changes only for `previousPrice`) |
| `priceChangePercent` | number | Signed percentage, rounded to two decimals. Negative for a price drop |
| `available`, `previousAvailable` | boolean | `true` when at least one variant can be bought |
| `variantChanges` | array | The variants that changed, with old and new values |
| `detectedAt` | string | ISO 8601 time the change was detected |

Every run also writes one store row per store, with `status` (`baseline_saved`, `no_changes`, `changes_found`, or a failure code such as `not_shopify`, `access_denied`, `blocked_by_robots`), the number of products read and the number of changes:

```json
{
  "recordType": "store",
  "store": "allbirds.com",
  "storeName": "Allbirds",
  "currency": "USD",
  "status": "no_changes",
  "productsFound": 337,
  "changesFound": 0,
  "checkedAt": "2026-11-03T08:00:04.120Z",
  "previousCheckedAt": "2026-11-02T08:00:03.870Z"
}
```

In snapshot mode every product is a `recordType: "product"` row with `title`, `vendor`, `productType`, `price`, `maxPrice`, `compareAtPrice`, `available`, `variantsCount`, `tags`, `createdAt`, `publishedAt`, `updatedAt` and, with **Include variants**, the variant list. A run summary (stores checked and failed, changes delivered, warnings) is saved under the key `RUN_SUMMARY` in the run's key-value store.

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

This Actor uses **pay-per-event** pricing. You pay only for the events below. Platform usage is included. Prices per event on the Apify free plan (higher plans get lower prices):

| Event | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| Actor start (per run) | $0.001 | $0.001 | $0.001 | $0.001 |
| Catalog scan (per 1,000 products checked, at least one per store, changes mode) | $0.002 | $0.00175 | $0.00158 | $0.0014 |
| Detected change (changes mode) | $0.003 | $0.00263 | $0.00237 | $0.0021 |
| Product record (snapshot mode) | $0.0008 | $0.0007 | $0.00063 | $0.00056 |

Examples on the free plan:

- **One store with about 340 products, checked daily, no changes:** $0.001 + $0.002 = **$0.003 per run**, about $0.09 per month.
- **The same store checked every hour, no changes:** 24 runs × $0.003 = **$0.072 per day**, about $2.16 per month.
- **The same store with 5 changes in a run:** $0.003 + 5 × $0.003 = **$0.018**.
- **20 stores with about 1,000 products each, nothing changed:** $0.001 + 20 × $0.002 = **$0.041 per run**.
- **Snapshot of 1,000 products:** 1,000 × $0.0008 + $0.001 = **$0.801**.

You are charged per detected change, not per catalog product, so a big store with a few changes stays cheap. Stores that fail (not Shopify, hidden catalog, blocked by robots.txt) are not charged. You can cap any run with the run's maximum cost: when the cap is reached the Actor stops cleanly and keeps the old baseline for the unfinished store, so no change is lost.

### Shopify Store Monitor compared with other approaches

| | Shopify Store Monitor | Product scraper plus your own comparison | Manual checks |
|---|---|---|---|
| Output | Only the changes, one row each | Every product on every run; you build the diff | Whatever you notice |
| Baseline | Saved for you in your Apify account | You store and compare previous runs | None |
| Billing | Small fee per store and per detected change | Usually per product row on every run | Your time |
| Best for | Ongoing monitoring on a schedule | One-off exports and large catalog analysis | A single product, once |
| Stock quantities | No (yes/no per variant only) | Depends on the tool | Not visible to visitors either |

Use **Snapshot** mode of this Actor when you just need a one-off list of a store's products. Use **Changes** mode when you want to be told what moved.

### Tips for best results

- Run the first check once, then schedule it. A baseline is needed before changes can be found.
- Set **Minimum price change (%)** to 1 to 5 if a store adjusts prices by cents.
- Use **Only watch products matching** to follow one product line and keep runs smaller.
- For very large stores, raise **Max products per store**. When a store is cut off at the limit, removed-product detection is switched off for that run, because products beyond the limit would look removed.
- Use one **Watchlist name** per purpose. Changing the stores in a watchlist is fine: new stores get a baseline, existing ones keep theirs.

### Limits and good to know

- Only stores that publish `/products.json` work. Some stores turn it off, put the store behind a password, or block automated requests; they appear as `not_shopify` or `access_denied` rows and cost nothing.
- Stock is a yes/no flag per variant. Shopify does not publish stock quantities in the public catalog, so the Actor reports `OUT_OF_STOCK` when no variant can be bought and `BACK_IN_STOCK` when one can again.
- A product that disappears from the catalog is reported as `REMOVED_PRODUCT`. The store may have deleted it, unpublished it or hidden it; the Actor cannot tell which.
- A catalog that comes back **completely empty** after a saved baseline is reported as `empty_catalog` and does not mark every product removed. An empty list is often a password page or a region redirect, and charging a removal for each product would bill a glitch. The previous baseline is kept. The store row says why nothing was listed, including when your filters matched no product (`no_matches`).
- The baseline is stored in your account in a named key-value store (`shopify-store-monitor-state`). Deleting that store starts new baselines. A run that is refused permission to create the named store (HTTP 403) uses this run's default key-value store instead, finishes the check, and does not keep that snapshot for the next run.
- Prices are numbers in the store's currency (`currency` is read from the store's public shop info and can be `null`).
- Runs read the public catalog only: no cart, no checkout, no customer data, no admin access.

### Use it from AI agents, the API and code

**Apify MCP server (ChatGPT, Claude, Cursor and other MCP clients).** Connect `https://mcp.apify.com`, then follow these steps:

1. `search-actors` with a short query such as `Shopify price tracker`.
2. `fetch-actor-details` for `mmdincer/shopify-store-monitor` to read the input schema.
3. `call-actor` with the input below and a spending cap in `callOptions`.
4. `get-dataset-items` with the returned dataset ID. `call-actor` returns the dataset ID, not the rows.

```json
{
  "actor": "mmdincer/shopify-store-monitor",
  "input": {
    "stores": ["allbirds.com"],
    "mode": "changes",
    "monitorName": "agent-watchlist"
  },
  "callOptions": { "maxTotalChargeUsd": 0.5 }
}
```

The first call for a store only saves a baseline, so tell the user that changes appear from the second call. Keep `monitorName` the same between calls. This Actor uses limited permissions, so it is not blocked from the MCP server.

To give an MCP client this Actor as its own tool (no search step), connect `https://mcp.apify.com?tools=mmdincer/shopify-store-monitor`.

**JavaScript**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('mmdincer/shopify-store-monitor').call(
    { stores: ['allbirds.com'], mode: 'changes' },
    { maxTotalChargeUsd: 0.5 },
);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const changes = items.filter((row) => row.recordType === 'change');
```

**Python**

```python
import os
from decimal import Decimal
from apify_client import ApifyClient

client = ApifyClient(token=os.environ['APIFY_TOKEN'])
run = client.actor('mmdincer/shopify-store-monitor').call(
    run_input={'stores': ['allbirds.com'], 'mode': 'changes'},
    max_total_charge_usd=Decimal('0.5'),
)
items = client.dataset(run['defaultDatasetId']).list_items().items
changes = [row for row in items if row['recordType'] == 'change']
```

**HTTP (one request, returns the rows)**

```bash
curl -X POST "https://api.apify.com/v2/acts/mmdincer~shopify-store-monitor/run-sync-get-dataset-items?maxTotalChargeUsd=0.5" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"stores": ["allbirds.com"], "mode": "changes"}'
```

The synchronous endpoint waits for the run to finish, so use it for a few stores. For many stores, start the run asynchronously and read the dataset when it ends.

### Automation recipes

**Slack or email alert through a webhook.** In the Apify Console open **Integrations** (or **Webhooks**) and add a webhook on **Run succeeded**. In your handler fetch the run's dataset (the webhook payload contains the run, including `defaultDatasetId`) and keep the rows where `recordType` is `change`. Send one message per row, for example: `PRICE_DROP: {title} {previousPrice} -> {price} ({priceChangePercent}%) {url}`.

**Zapier.** Create a Zap with Apify as the trigger for a finished Actor or task run, add an Apify step that fetches the run's dataset items, filter for `recordType` equal to `change`, then add a Slack, Gmail or Google Sheets step. See the [Apify Zapier integration](https://docs.apify.com/integrations/zapier).

**Make.** Add the Apify module that runs an Actor (or watches for a finished run), then the module that gets dataset items, filter on `recordType` equal to `change` and route the bundles to your app. See the [Apify Make integration](https://docs.apify.com/integrations/make).

**Google Sheets or a database.** Fetch the dataset as JSON or CSV with the API (`/v2/datasets/{id}/items?format=csv`) after each run and append the change rows. The `changeType` and `detectedAt` columns make filtering and pivot tables easy.

**Scheduling.** Save your input as a task (for example `competitors`, daily at 08:00) and add a schedule in the Apify Console. Keep the same `monitorName` so every run compares with the previous one.

### Is it legal to monitor public Shopify stores?

The Actor reads only the product catalog that each store publishes to every visitor at `/products.json` and the store's public shop info. It does not log in, does not use any Shopify API or account, and does not collect personal data. By default it also reads each store's `robots.txt` and skips stores that disallow the catalog path. Store owners can still have their own terms of use, and Shopify's own terms restrict automated access to Shopify's services; you are responsible for using the results in line with the terms of the stores you monitor and with the laws that apply to you. This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by Shopify Inc. or any store it reads. All trademarks belong to their owners.

**Trademarks.** Not affiliated with, endorsed by, or sponsored by Shopify. Trademarks belong to their respective owners. The Shopify name and logo are used only to say which platform the Actor reads; the icon shows the Shopify mark for the same purpose.

### FAQ

**How do I track price changes on a Shopify store?** Enter the store domain, run the Actor once to save a baseline, then run it again on a schedule. Every price move appears as a `PRICE_DROP` or `PRICE_INCREASE` row with the old and new price.

**How do I get Shopify restock alerts?** Set `changeTypes` to `OUT_OF_STOCK` and `BACK_IN_STOCK`, schedule the Actor and send the rows to a webhook, Slack or email. A product counts as back in stock when at least one variant can be bought again.

**Can I monitor competitor Shopify stores?** Yes. Add the competitor domains to `stores` and give the watchlist a name such as `competitors`. Only public catalog data is read.

**Does it need an API key, cookies or a login?** No.

**Why did my first run return no changes?** The first run of a store saves the baseline and returns a `baseline_saved` store row. Changes appear from the next run with the same `monitorName`.

**How often should I run it?** As often as you need the alerts. Daily is common for price monitoring; hourly can make sense for restock alerts on drops. Each run only compares with the previous run.

**How is this different from a Shopify scraper?** A scraper returns the whole catalog on every run. This Actor compares each run with the previous one and returns only the detected changes, which is what you pay for. If you need the full catalog once, use Snapshot mode.

**Can I watch just one collection?** Yes, enter a collection URL such as `https://store.com/collections/sale`.

**Can I get all products instead of changes?** Yes: set **Mode** to `Snapshot (full product list)`.

**Does it show stock quantities?** No. Shopify's public catalog only says whether a variant can be bought.

**Does it work for stores that are not on Shopify?** No. WooCommerce, Magento and marketplace listings are not supported.

**What happens when a run stops at its maximum cost?** Stores that were not finished keep their old baseline, so their changes are reported in the next run.

**Why is a store listed as `not_shopify` or `access_denied`?** The store has no public `/products.json`, sits behind a password page, or refuses automated requests. Try again later, lower `requestsPerSecond`, or enable Apify Proxy.

**Can an AI agent use this Actor?** Yes. It works through the Apify MCP server, the Apify API and the Apify clients; see "Use it from AI agents, the API and code". Set a maximum charge per run.

**How do I report a problem or ask for a feature?** Open an issue in the **Issues** tab and include a link to your run.

# Changelog

This Actor's version history is a separate document: https://apify.com/mmdincer/shopify-store-monitor/changelog.md

# Actor input Schema

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

Shopify stores to watch, one per line: a domain such as <code>allbirds.com</code> or a store URL. A collection URL such as <code>https://allbirds.com/collections/mens</code> watches only that collection. The store must publish its catalog at <code>/products.json</code> (most Shopify stores do). Up to 200 stores per run.

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

<b>Changes</b> compares each store with the previous run and returns only what changed (the first run saves a baseline). <b>Snapshot</b> returns every product of the store as it is now, without comparing or saving anything.

## `changeTypes` (type: `array`):

Which changes become rows (changes mode only). Leave all selected to get everything. Changes you leave out are not reported and are not charged, and they are not reported later either.

## `minPriceChangePercent` (type: `number`):

Ignore price changes smaller than this percentage, for example <code>5</code> to report only moves of 5% or more. <code>0</code> reports every price change.

## `includeKeywords` (type: `array`):

Optional. Watch only products whose title, brand, product type or tags contain at least one of these words (not case sensitive), for example <code>sneaker</code>. Leave empty to watch the whole catalog. Changing this list saves a new baseline on the next run.

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

Optional. Skip products whose title, brand, product type or tags contain any of these words, for example <code>gift card</code>. Changing this list saves a new baseline on the next run.

## `monitorName` (type: `string`):

Name of the saved baseline (changes mode). Runs with the same name compare against each other. Use different names to keep separate watchlists on the same stores, for example <code>competitors</code> and <code>suppliers</code>.

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

Upper limit of products read from one store. Stores with more products are cut off, and removed-product detection is switched off for that run because products beyond the limit would look removed.

## `includeVariants` (type: `boolean`):

In snapshot mode, add the list of variants (SKU, price, compare-at price, availability) to every product row. Change rows always contain the variants that changed.

## `respectRobotsTxt` (type: `boolean`):

Read each store's <code>robots.txt</code> first and skip stores that disallow the catalog path. Recommended. Stores that are skipped are not charged.

## `requestsPerSecond` (type: `number`):

How fast pages are requested from one store. Each page holds up to 250 products. Lower it if a store starts answering with HTTP 429. The Actor also slows down by itself when a store pushes back.

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

Optional. Direct requests work for almost all stores. Turn on Apify Proxy only if a store refuses requests from the Apify platform. Proxy traffic is billed by Apify separately.

## Actor input object example

```json
{
  "stores": [
    "allbirds.com",
    "colourpop.com"
  ],
  "mode": "changes",
  "changeTypes": [
    "NEW_PRODUCT",
    "PRICE_DROP",
    "PRICE_INCREASE",
    "OUT_OF_STOCK",
    "BACK_IN_STOCK",
    "REMOVED_PRODUCT"
  ],
  "minPriceChangePercent": 0,
  "includeKeywords": [
    "sneaker"
  ],
  "excludeKeywords": [
    "gift card"
  ],
  "monitorName": "default",
  "maxProductsPerStore": 5000,
  "includeVariants": false,
  "respectRobotsTxt": true,
  "requestsPerSecond": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per detected change: new product, price change, sell-out, restock or removal (changes mode). The view also lists one store summary row per store; keep rows with recordType change.

## `stores` (type: `string`):

One row per store with the outcome, product count and number of changes. Failed stores show the reason here.

## `products` (type: `string`):

One row per product when Mode is Snapshot.

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

Stores checked and failed, products and changes delivered, warnings and the reason the run stopped.

# 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": [
        "allbirds.com"
    ],
    "mode": "changes",
    "changeTypes": [
        "NEW_PRODUCT",
        "PRICE_DROP",
        "PRICE_INCREASE",
        "OUT_OF_STOCK",
        "BACK_IN_STOCK",
        "REMOVED_PRODUCT"
    ],
    "minPriceChangePercent": 0,
    "monitorName": "default",
    "maxProductsPerStore": 5000,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mmdincer/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 = {
    "stores": ["allbirds.com"],
    "mode": "changes",
    "changeTypes": [
        "NEW_PRODUCT",
        "PRICE_DROP",
        "PRICE_INCREASE",
        "OUT_OF_STOCK",
        "BACK_IN_STOCK",
        "REMOVED_PRODUCT",
    ],
    "minPriceChangePercent": 0,
    "monitorName": "default",
    "maxProductsPerStore": 5000,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("mmdincer/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 '{
  "stores": [
    "allbirds.com"
  ],
  "mode": "changes",
  "changeTypes": [
    "NEW_PRODUCT",
    "PRICE_DROP",
    "PRICE_INCREASE",
    "OUT_OF_STOCK",
    "BACK_IN_STOCK",
    "REMOVED_PRODUCT"
  ],
  "minPriceChangePercent": 0,
  "monitorName": "default",
  "maxProductsPerStore": 5000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call mmdincer/shopify-store-monitor --silent --output-dataset

```

## MCP server setup

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