# Shopify Price Change Monitor (`rowcrate/shopify-price-change-monitor`) Actor

Run it on a schedule and get only what changed: price drops, price rises, sold out, back in stock, new and removed products.

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

## Pricing

from $1.00 / 1,000 change detecteds

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

## Shopify Price Change Monitor

Run it on a schedule. Get back **only what changed** since last time — price drops, price
rises, sold out, back in stock, new products, removed products.

Not a catalogue export. A change report.

### The difference, in one line

A 4,000-row spreadsheet every Monday is work you hand your team. *"22 of the products you
track dropped price, the deepest by 23%"* is something they act on before lunch.

### What people use it for

**Competitive pricing.** Know the morning a competitor discounts, not three weeks later
when someone happens to notice.

**Promotion intelligence.** `price_dropped` and `price_change_pct` show how deep a
discount really is against the price it is discounted from — not against the banner.

**Stock signals.** `stock_change` flags both directions. A product that sells out every
week and comes back is a product the store keeps under-buying.

**Watching your own catalogue.** Price typos, variants that quietly went out of stock,
products that disappeared during a theme migration.

### What you get back

| change\_type | changed\_fields | product\_title | variant\_title | previous\_price | price | price\_change\_pct | stock\_change |
|---|---|---|---|---|---|---|---|
| changed | price | Merino Runner | US 9 / Grey | 128.00 | 99.00 | -22.7 | |
| changed | available | Trail Cap | Default Title | 32.00 | 32.00 | | went\_out\_of\_stock |
| changed | price, available | Alpine Shell | S / Storm | 245.00 | 199.00 | -18.8 | back\_in\_stock |
| new | | Wool Beanie | One Size | | 38.00 | | |
| removed | | Old Tee | M / Black | 24.00 | 24.00 | | |

Every row also carries the full product record — store, SKU, product URL, compare-at price,
vendor, image — plus `source_url`, `collected_at` and `record_hash`.

`change_type` is one of `new`, `changed`, `removed` — or `baseline` on the very first run.

### Read this before your first run

**The first run has nothing to compare against.** It reads the full catalogue, saves it, and
returns every row marked `baseline`. `RUN_SUMMARY` says `"baseline": true`.

**The second run is where the product starts.** From then on you get only the differences.

This is not a limitation to work around — it is how any change feed works. Schedule it
weekly and by run two you are getting exactly what you came for.

### Settings

| Setting | Default | What it does |
|---|---|---|
| **Shopify stores to watch** | — | Paste each store's homepage. Keep the list stable between runs. |
| **Tracker name** | default | How the actor remembers what it saw. One name per group you follow. Changing it starts over. |
| **Max products per store** | 0 (whole catalogue) | Leave at 0. A partial read makes unread products look removed. |
| **Requests per second** | 1 | Polite by default. |

**Tip:** use a separate tracker name per group of stores — `shoes-competitors`,
`my-own-store`. They keep independent memory and never interfere.

### What it costs

| Event | Price |
|---|---|
| Run started | $0.01 |
| Store checked | $0.03 |
| Change detected | $0.001 |

Five stores in a quiet week (about 60 changes) costs roughly **$0.22**. The same five in a
Black Friday week with 900 changes costs about **$1.06**. The cost follows the value: a week
where nothing moved costs almost nothing, and the baseline run is never charged for changes.

### Good to know

- **A failed run never overwrites the memory.** If a store blocks us or `robots.txt` closes
  mid-run, the previous snapshot is kept. Otherwise the next run would report as *removed*
  everything that was merely unread — and you would act on a report that is wrong.
- **Public data only.** It reads the same public product endpoint your browser hits when it
  loads a collection page. No login, no customer data, no orders.
- **`robots.txt` respected.** A store that asks crawlers to stay out is skipped and named.
- **Sites that are not on Shopify are reported**, not silently dropped — check the `SKIPPED`
  record in the key-value store.
- **Currency is not published** by Shopify's public catalogue, so prices come back as plain
  numbers in each store's own currency. Set it per store on your side if you compare across
  countries.

### Questions?

Open an issue and I answer within 12 hours. Want the change report delivered as a file to
your inbox or a Google Sheet every Monday morning instead of pulling it yourself? That is
what I do — **rowcrate.com**.

# Actor input Schema

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

The stores you want to keep an eye on. Paste the homepage of each one. Keep this list stable between runs - adding a store makes its whole catalogue show up as new next time, which is correct but noisy.

## `trackerName` (type: `string`):

A label for this set of stores. It is how the actor remembers what it saw last time. Use one name per group you follow - 'shoes-competitors', 'my-own-store'. Changing it starts over from scratch, which is the way to reset if the list of stores changes a lot.

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

0 means the whole catalogue, which is what you usually want here - a partial read makes missing products look like they were removed. Set a limit only while you are testing.

## `requestsPerSecond` (type: `integer`):

How fast to read each store. The default is polite and works everywhere.

## Actor input object example

```json
{
  "storeUrls": [
    "https://allbirds.com",
    "https://competitor.com"
  ],
  "trackerName": "shoes-competitors",
  "maxProductsPerStore": 0,
  "requestsPerSecond": 1
}
```

# Actor output Schema

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

Only what moved: price drops and rises, sold out, back in stock, new and removed products. The first run is a baseline and returns the full catalogue.

## `runSummary` (type: `string`):

Row count, request count, and the error categories that came up. Read this first when a run looks off.

## `skipped` (type: `string`):

Targets that returned nothing and why. Showing the gap is what makes the rest of the file trustworthy.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "storeUrls": [
        "https://gymshark.com"
    ]
};

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

# Run the Actor and wait for it to finish
run = client.actor("rowcrate/shopify-price-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 '{
  "storeUrls": [
    "https://gymshark.com"
  ]
}' |
apify call rowcrate/shopify-price-change-monitor --silent --output-dataset

```

## MCP server setup

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