# Shopify Product Scraper & Price Change Monitor (`feedwright/shopify-catalog-change-monitor`) Actor

Scrape any Shopify store: every product, variant, price and stock level as clean rows. Run it on a schedule and get only what changed — price moves, new products, and out-of-stock alerts.

- **URL**: https://apify.com/feedwright/shopify-catalog-change-monitor.md
- **Developed by:** [Feedwright Studio](https://apify.com/feedwright) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 product variant rows

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

Get every product, variant, price and stock level from any Shopify store as clean, exportable rows.
Then put it on a schedule and get told only what changed — new products, price moves, and anything
that went out of stock.

### Who this is for

- **Ecommerce and marketing agencies** keeping an eye on a client's competitors
- **Brand and pricing teams** who need to know the moment a rival discounts
- **Retail and wholesale buyers** tracking stock and new releases across suppliers
- **Analysts and researchers** who need a catalogue as a spreadsheet, not a screenshot

If you have ever asked someone to "check their prices again" or kept a manual list of SKUs in a
spreadsheet, this replaces that job.

### Two modes

#### Full catalogue

Paste one or more store addresses and get back one row per variant: product title, variant, SKU,
price, compare-at price, stock status, vendor, type, tags, image and a direct link.

#### Changes only

Run it on a schedule. Each run compares against the previous one for that store and returns just
the differences:

`new_product` · `removed_product` · `new_variant` · `removed_variant` · `price_increase` ·
`price_decrease` · `back_in_stock` · `out_of_stock` · `title_changed`

Every change row carries the old value and the new value, so a price move reads as
`14.00 → 11.20`, not just "something changed."

### Try it

Run it with the defaults. It will read a real store and return its full catalogue so you can see
the exact shape of the output before configuring anything.

Then paste your own store addresses. **Any format works** — a plain domain, a full product URL, a
collection page with query parameters. It is all reduced to the right store.

### Example output

One row per variant:

| store | product\_title | variant\_title | sku | price | compare\_at\_price | available |
|---|---|---|---|---|---|---|
| examplestore.com | Cold Brew Concentrate | 32 oz | CB-32 | 24.00 | 28.00 | true |
| examplestore.com | Cold Brew Concentrate | 64 oz | CB-64 | 41.00 | | false |

And in changes-only mode:

| change\_type | product\_title | variant\_title | field | old\_value | new\_value |
|---|---|---|---|---|---|
| price\_decrease | Cold Brew Concentrate | 32 oz | price | 28.00 | 24.00 |
| out\_of\_stock | Cold Brew Concentrate | 64 oz | available | true | false |
| new\_product | Winter Blend Whole Bean | | | | |

### What you pay for

Pricing is per delivered result, and you are only charged for results you actually receive.

| Mode | What you pay for |
|---|---|
| Full catalogue | Each product row returned |
| Changes only | Each store successfully checked, plus each change found |

**You are never charged for:**

- A run that fails
- A store that cannot be reached, is rate limiting, or is not a Shopify store
- A run that returns nothing
- **Changes that did not happen.** That includes the first run in changes-only mode: it has no
  previous state to compare against, so it reports no changes and bills none.

The first changes-only run does still pay the store check, because the store was read and compared.
That store check is the only charge on a baseline run.

In changes-only mode you pay for the changes, **not** for the full catalogue that has to be read in
order to find them. Watching a six-thousand-variant store that moved three prices costs you three
changes, not six thousand rows.

### Limits and what it will not do

- Reads **public storefront catalogue data only** — the same product information any shopper sees.
  It does not touch customer data, orders, or anything behind a login.
- A small number of stores turn their public product feed off. When that happens you are told
  clearly, and not charged.
- Some stores rate limit aggressively. Enable the datacenter proxy option if you hit that; most
  stores do not need it.
- There is a maximum-products-per-store ceiling so a very large catalogue cannot produce a
  surprise bill. Raise it when you need more.
- If one store in a list fails, the others still run. You get the results that worked plus an
  honest note about the one that did not.

### Getting your results out

Results are available as JSON, CSV, Excel, or through the API, and can be pushed to your own
systems with a webhook. The run summary records what was collected, what changed, and any store
that failed along with the reason.

### Scheduling

Changes-only mode is built to be scheduled. Daily or weekly both work well. Each run remembers the
previous state per store, so you can add and remove stores from the list without losing history for
the others.

### Support

Found a problem or need a field that is not here? Open an issue on this tool. Issues are answered
the same business day.

# Actor input Schema

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

One or more Shopify store addresses. Paste the plain domain or any page URL from the store — both work.

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

Full catalogue returns every product and variant. Changes only compares against the previous run for the same store and returns just what moved — new products, price changes, and stock flips.

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

A safety ceiling so a very large store cannot produce a surprise bill. Raise it if a catalogue is bigger than this.

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

Changes-only mode. Leave empty to receive everything.

## `useProxy` (type: `boolean`):

Only helps if a store is rate limiting you. Leave off unless you need it — it is not required for most stores.

## Actor input object example

```json
{
  "stores": [
    "deathwishcoffee.com"
  ],
  "mode": "snapshot",
  "maxProductsPerStore": 1000,
  "changeTypes": [],
  "useProxy": false
}
```

# Actor output Schema

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

Catalogue rows in full-catalogue mode, or change rows in changes-only mode.

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

What was collected, what changed, and anything that failed with the reason why.

# 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": [
        "deathwishcoffee.com"
    ],
    "mode": "snapshot",
    "maxProductsPerStore": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("feedwright/shopify-catalog-change-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": ["deathwishcoffee.com"],
    "mode": "snapshot",
    "maxProductsPerStore": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("feedwright/shopify-catalog-change-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": [
    "deathwishcoffee.com"
  ],
  "mode": "snapshot",
  "maxProductsPerStore": 1000
}' |
apify call feedwright/shopify-catalog-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,feedwright/shopify-catalog-change-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/frMwXk6UK0XLuoIfF/builds/g66G97OpAULdPzFqw/openapi.json
