# Shopify Catalog Delta Monitor — Product, Price & Stock Changes (`ceddl/shopify-catalog-delta`) Actor

Monitor public Shopify catalogs and receive deterministic product and variant change events between scheduled observations.

- **URL**: https://apify.com/ceddl/shopify-catalog-delta.md
- **Developed by:** [Cedric Günther](https://apify.com/ceddl) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 product processeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Shopify Catalog Delta Monitor do?

**Shopify Catalog Delta Monitor turns public storefront catalogs into deterministic product and variant change events.** Give it one or more Shopify store URLs and a user-owned Apify key-value store. The first successful run creates a baseline; scheduled runs then emit only meaningful additions, removals, price changes, compare-at-price changes, availability changes, and selected metadata changes.

The Actor is designed for competitor catalog monitoring, retail intelligence, assortment tracking, and automation through the Apify API, schedules, webhooks, and integrations. It uses ordinary public HTTP access only. It does not log in, access Shopify Admin, collect customer or checkout data, solve CAPTCHAs, or attempt to defeat blocking.

### Why use Shopify Catalog Delta Monitor?

- Receive machine-readable change records instead of repeatedly downloading and diffing full catalogs.
- Track multiple stores without one failed store terminating the others.
- Keep monitoring state in a key-value store that you select and own.
- Use deterministic event IDs to deduplicate retries downstream.
- Reset or replace a baseline deliberately without generating false changes.
- Optionally export a full normalized snapshot alongside deltas.

### How to monitor Shopify catalog changes

1. Create a named key-value store in your Apify account for durable baselines.
2. Open the Actor's **Input** tab.
3. Add each public storefront URL and a stable `watchId`.
4. Select your baseline key-value store.
5. Run once to establish the baseline.
6. Create an Apify task and schedule it hourly or daily with the same input.
7. Consume `DELTA` records from each run's default dataset or trigger a webhook when the run finishes.

Runs that use the same baseline store and `watchId` may overlap safely: the Actor serializes them with an exclusive, renewable request-queue lease. A later run waits for the active run to commit before it reads the baseline. Different watches can still run concurrently.

### Input

```json
{
    "stores": [{ "url": "https://example-store.com", "watchId": "example-store" }],
    "baselineStoreId": "YOUR_KEY_VALUE_STORE_ID",
    "baselineMode": "persistent",
    "includeVariants": true,
    "emitFullSnapshot": false
}
```

`baselineMode` accepts:

- `persistent`: compare with the existing baseline and advance it after a complete successful observation.
- `replace`: fetch and atomically replace the baseline without emitting deltas.
- `reset`: delete this watch's baseline without requesting the storefront.

Advanced inputs control store concurrency, request timeout, inter-page delay, and a fail-closed catalog page limit.

### Output

```json
{
    "recordType": "DELTA",
    "eventId": "2f93cc...",
    "eventType": "VARIANT_PRICE_CHANGED",
    "watchId": "example-store",
    "storeUrl": "https://example-store.com",
    "productIdentity": "id:123",
    "variantIdentity": "id:456",
    "previousValue": "29.99",
    "currentValue": "24.99",
    "detectedAt": "2026-09-15T12:00:00.000Z",
    "context": {
        "currentProduct": { "id": "123", "handle": "example", "title": "Example" }
    }
}
```

The dataset can also contain `SNAPSHOT`, `STORE_SUMMARY`, `STORE_ERROR`, and `RUN_SUMMARY` records. The run's default key-value store contains the aggregate summary under `OUTPUT`. You can download the dataset as JSON, JSONL, CSV, Excel, XML, RSS, or HTML.

### Data table

| Field                            | Meaning                                                                          |
| -------------------------------- | -------------------------------------------------------------------------------- |
| `eventType`                      | Stable change category such as `PRODUCT_ADDED` or `VARIANT_AVAILABILITY_CHANGED` |
| `eventId`                        | Deterministic SHA-256 transition ID for downstream deduplication                 |
| `watchId`                        | User-defined monitoring stream identifier                                        |
| `productIdentity`                | Shopify product ID when public, otherwise a documented fallback                  |
| `variantIdentity`                | Shopify variant ID when public, otherwise a documented fallback                  |
| `previousValue` / `currentValue` | The changed scalar, range, variant, or product                                   |
| `context`                        | Compact normalized product context                                               |
| `detectedAt`                     | Observation time, not the merchant's edit time                                   |

Normalized snapshot products include IDs, handle, URL, title, vendor, product type, stable sorted tags, availability, price ranges, compare-at price ranges, featured image, and optional nested variants. Variants include public ID, title, public SKU, price, compare-at price, availability, and named option values.

### How much does it cost to monitor Shopify catalogs?

Monetization is not enabled during private validation. The prepared Store model is pay per event with platform usage included: `$0.0008` for each successfully processed product, `$0.008` for each persisted delta, and the Apify-managed `$0.00005` Actor-start event. Failed stores, retries, snapshot rows, and summary rows are not custom-charged. The code stops starting new storefront work when the run's event budget is reached. These prices apply only after a separately authorized monetization launch; check the Pricing tab for the active configuration.

### Advanced reliability notes

Baselines are sharded into bounded records and committed by switching a manifest only after the complete catalog and primary dataset records succeed. Failed, blocked, malformed, stalled, or page-limited observations preserve the prior baseline. Products and variants are sorted by stable identity, and tags/options are normalized so ordering alone does not create changes.

Runs that target the same baseline store, normalized URL, and `watchId` are strictly serialized with an Apify request-queue lease. A later run waits until the active run finishes, then reads the newly committed baseline before computing deltas. Leases are renewed while work is active and expire automatically after an abandoned run, so a crashed run cannot permanently block a watch. Different watches use different lock queues and remain independently runnable.

Keep the same normalized URL and `watchId` for a monitoring stream. Changing either intentionally creates a different state key. Use `replace` after an intentional scope change.

### FAQ, limitations, and support

**Does this work on every Shopify site?** No. Custom storefronts, password-protected stores, stores that disable public catalog routes, robots exclusions, geographic variation, and explicit blocking can prevent or limit access. The Actor reports these conditions and does not bypass them.

**Does availability mean inventory quantity?** No. It is the public purchasability flag exposed by the storefront. Exact stock quantities are not claimed.

**Are changes real-time?** Changes are detected between observations. Detection time depends on your schedule and does not prove when the merchant made the edit.

**Is scraping legal?** You are responsible for your use case and for respecting applicable law, site terms, and robots instructions. Monitor only public product information you are permitted to access.

Use the Actor's Issues tab to report a reproducible storefront compatibility problem. Include the public store URL, run ID, and error code; never include credentials or customer data.

# Actor input Schema

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

Each watch ID and normalized store URL form one isolated monitoring stream.

## `baselineStoreId` (type: `string`):

Optional for snapshot-only first runs. Select a key-value store in your account for durable cross-run monitoring; otherwise the run's default store is used.

## `baselineMode` (type: `string`):

Persistent compares and advances state; replace writes a fresh baseline without deltas; reset deletes state without accessing the store.

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

Normalize variants and emit variant-level changes.

## `emitFullSnapshot` (type: `boolean`):

Also write one SNAPSHOT dataset record per current product.

## `maxConcurrency` (type: `integer`):

Number of storefronts processed at once. Requests within each storefront remain sequential.

## `requestDelayMillis` (type: `integer`):

Polite delay applied between sequential public catalog page requests for each store.

## `requestTimeoutSecs` (type: `integer`):

Timeout for each ordinary public HTTP request before controlled retry or store-level failure.

## `maxPagesPerStore` (type: `integer`):

The baseline is preserved and the store fails if this limit is reached before pagination completes.

## Actor input object example

```json
{
  "stores": [
    {
      "url": "https://deathwishcoffee.com",
      "watchId": "store-qa"
    }
  ],
  "baselineMode": "persistent",
  "includeVariants": true,
  "emitFullSnapshot": true,
  "maxConcurrency": 1,
  "requestDelayMillis": 250,
  "requestTimeoutSecs": 30,
  "maxPagesPerStore": 10
}
```

# Actor output Schema

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

No description

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

No description

# 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": [
        {
            "url": "https://deathwishcoffee.com",
            "watchId": "store-qa"
        }
    ],
    "baselineMode": "persistent",
    "includeVariants": true,
    "emitFullSnapshot": true,
    "maxConcurrency": 1,
    "requestDelayMillis": 250,
    "requestTimeoutSecs": 30,
    "maxPagesPerStore": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("ceddl/shopify-catalog-delta").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": [{
            "url": "https://deathwishcoffee.com",
            "watchId": "store-qa",
        }],
    "baselineMode": "persistent",
    "includeVariants": True,
    "emitFullSnapshot": True,
    "maxConcurrency": 1,
    "requestDelayMillis": 250,
    "requestTimeoutSecs": 30,
    "maxPagesPerStore": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("ceddl/shopify-catalog-delta").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": [
    {
      "url": "https://deathwishcoffee.com",
      "watchId": "store-qa"
    }
  ],
  "baselineMode": "persistent",
  "includeVariants": true,
  "emitFullSnapshot": true,
  "maxConcurrency": 1,
  "requestDelayMillis": 250,
  "requestTimeoutSecs": 30,
  "maxPagesPerStore": 10
}' |
apify call ceddl/shopify-catalog-delta --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ceddl/shopify-catalog-delta"
        }
    }
}
```

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/cUqsW9jsr2ai1ucWk/builds/9btqEUqxrdE8agzlS/openapi.json
