# Shopify Competitor Price & Stock Monitor | WooCommerce (`saahir-job-data/shopify-woocommerce-product-monitor`) Actor

Monitor Shopify competitor prices, stockouts and restocks with automatic rolling comparisons and optional change webhooks. Includes WooCommerce catalog snapshots, variant details where published, CSV exports and coverage reports. $3/1,000 product observations + $0.001/run.

- **URL**: https://apify.com/saahir-job-data/shopify-woocommerce-product-monitor.md
- **Developed by:** [Saahir Sultan](https://apify.com/saahir-job-data) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 observed products

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 Competitor Price & Stock Monitor do?

Track competitor price changes, stockouts and restocks from public Shopify catalogs. **Rolling mode remembers previous observations automatically:** save a task, choose a schedule and receive relevant changes through your webhook. WooCommerce Store API catalogs are also supported, with the variation limits explained below.

For ecommerce agencies, merchandising teams and automation developers who need product observations with source URLs, timestamps and explicit before/after values. Export JSON or CSV, or connect change notifications to Make or n8n.

**Price: $3 per 1,000 delivered product observations + $0.001 per run.** Platform usage is included in this Actor's usage price. One product with nested variants counts as one observation. Successful unchanged products are billed too. Failed stores produce no product-result charges; the run fee still applies.

Supports public [Shopify](https://www.shopify.com/) catalogs and compatible [WooCommerce](https://woocommerce.com/) Store API catalogs. Apify provides API access, saved tasks, scheduling and run reports.

### Why use this Shopify competitor monitor?

| Capability | Shopify | WooCommerce Store API |
| --- | --- | --- |
| Public product title, URL, description, images | Yes, where published | Yes, where published |
| Product prices and catalog stock status | Yes, where published | Yes, where published |
| Variant prices, SKUs and availability | Yes, where published | Usually unavailable from the parent catalog response |
| Automatic comparison between runs | Rolling mode | Rolling mode |
| Price/stock change webhooks | Comparable prices and known stock transitions | Comparable product prices and known product stock transitions |
| One-off catalog snapshots and selected-dataset comparison | Manual mode | Manual mode |

Requests use compatible public catalog endpoints. No Shopify admin key, WooCommerce API key, proxy subscription or browser setup is required. Stores must expose these endpoints and allow the requests. For a WooCommerce installation in a subdirectory, use its WordPress installation root.

### How to use Shopify price and stock monitoring

1. Enter the **store root URL**, for example `https://www.lttstore.com/`. Product and collection URLs are not accepted.
2. Choose **Rolling price and stock monitoring** and enter a stable **Monitor ID**, such as `my-shopify-competitors`.
3. Set a small product limit for the first run. If you have independently confirmed the store's currency, enter its uppercase code; otherwise leave it blank.
4. Run once to establish the baseline. Check the product dataset and **REPORT** for store compatibility and coverage. The baseline does not trigger price/stock alerts.
5. Save this configuration as an Apify task. Schedule that task daily at your preferred time and time zone. Keep the same Monitor ID between runs. [Apify scheduling guide](https://docs.apify.com/actors/running/schedules)
6. Optionally add a public HTTPS webhook you control. Use Make or n8n to turn the received changes into email, Slack messages or another downstream action.

```json
{
  "storeUrls": ["https://www.lttstore.com/"],
  "platform": "shopify",
  "monitoringMode": "rolling",
  "monitorId": "my-shopify-competitors",
  "maxProducts": 20,
  "maxProductsPerStore": 20,
  "maxSeconds": 120
}
```

This example intentionally omits currency and the optional webhook. It still records raw prices and can detect confirmed stock changes. Add `"shopifyCurrency": "USD"` only if you have confirmed that the monitored catalog uses USD. Example storefront access may change over time.

**Running this Actor does not create a schedule.** Customers create and control their own schedules. There is no always-on process and the Actor does not send email directly.

### Input

| Setting | Default | Meaning |
| --- | --- | --- |
| `storeUrls` | Required | 1–50 public store root URLs; exact normalized duplicates are removed. |
| `platform` | `auto` | `auto`, `shopify` or `woocommerce`. Auto tries the other platform only when the first catalog endpoint is absent or unsupported. |
| `monitoringMode` | `manual` | `manual` preserves existing snapshot behavior; `rolling` maintains comparison state automatically. |
| `monitorId` | Empty | Required for rolling. 1–80 characters: letters, numbers, underscores or hyphens; first character must be a letter or number. |
| `webhookUrl` | Empty | Optional public HTTPS JSON receiver in rolling mode. Stored as secret input. |
| `previousDatasetId` | Empty | Manual mode only: one prior dataset from this Actor, up to 10,000 rows or 12 MB of comparison data. |
| `shopifyCurrency` | Empty | Confirmed uppercase currency code applying to every Shopify store in this run, such as `USD`. |
| `maxProducts` | `500` | Total delivered observations; 1–10,000. |
| `maxProductsPerStore` | `100` | Per-store cap; 1–5,000. |
| `maxSeconds` | `240` | Catalog processing budget; 10–480 seconds. |

Use rolling mode without `previousDatasetId`. Keep the Apify run timeout above the catalog processing budget to allow state reads, exports, persistence and optional notifications. Store requests are sequential and rate limited. Customer spending limits can stop collection before the product caps are reached.

### Output

Example excerpt generated from a controlled test product: a price rises from $10.50 to $12.50 and its Blue / M variant becomes unavailable. **This is an example, not an observed merchant change.** The actual `changes` array also includes affected aggregate and variant fields.

```json
{
  "title": "Example Blue Hat",
  "url": "https://example.com/products/blue-hat",
  "currency": "USD",
  "priceMin": "12.5",
  "inStock": false,
  "changeType": "changed",
  "changes": [
    {
      "field": "priceMin",
      "before": "10.5",
      "after": "12.5"
    },
    {
      "field": "variants[2].inStock",
      "before": true,
      "after": false
    }
  ]
}
```

Each dataset row represents one product successfully observed during the current run.

Prices are decimal strings, such as `"19.95"`, to preserve precision. Missing data remains `null` or an empty collection. JSON retains nested variants and detailed changes.

Additional records in each run's key-value store:

- **PRODUCTS\_CSV** — flat product export; spreadsheet formula-like cells are escaped.
- **REPORT** — delivered/changed totals, per-store coverage, request counts, stopping reasons and rolling-monitor results where applicable.
- **UNRESOLVED** — incomplete or unsuccessful store scans and their reasons.
- **NOT\_OBSERVED** — prior products not observed this time, without claiming deletion or a stockout.
- **CHANGES** — confirmed price/stock events detected in this rolling run, even without a webhook.
- **MONITOR\_STATUS** — rolling state and delivery results, also available as `REPORT.monitor`: `eventsDetected`, `eventsDelivered`, `eventsPending`, `deliveryIssue`, `baselineGeneration` and the persistent `stateStoreId`.

`CHANGES` and `MONITOR_STATUS` are produced in rolling mode. `productsChanged` in REPORT includes other catalog changes too, so it need not equal the number of price/stock events.

The first observation is `baseline`. Matching prior products become `unchanged` or `changed`. `new_to_comparison` means absent from the chosen comparison state, not necessarily newly launched by the merchant. Observation timestamps alone do not trigger a change.

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Data table

| Fields | Contents |
| --- | --- |
| `key`, `platform`, `storeUrl`, `productId` | Stable identity and source store. |
| `title`, `url`, `sku`, `brand`, `productType` | Product details where published. |
| `currency`, `currencySource`, `priceMin`, `priceMax`, `regularPriceMin` | Monetary values and their context. |
| `inStock`, `variants`, `variantCount`, `variantsTruncated` | Catalog availability and available variation details. |
| `descriptionText`, `images` | Cleaned description and up to 10 image URLs. |
| `changeType`, `changes`, `changeCount` | Comparison status and exact before/after values. |
| `observedAt`, `previousObservedAt`, `sourceUrl` | Current/prior observation timestamps and source endpoint. |

### Pricing / Cost estimation

| Delivered observations | Runs | Actor usage charge |
| --- | --- | --- |
| 20 | 1 | $0.061 |
| 100 | 1 | $0.301 |
| 1,000 | 1 | $3.001 |
| 100 per day for 30 days | 30 | $9.03 |

These are Actor usage charges in USD at $0.003 per delivered observation plus $0.001 per run. Unchanged observations are charged because they still require collection. Comparison and reports do not add product-result charges. Customer plan fees, external workflow services and applicable taxes are separate. These examples are customer spending, not developer profit.

### Tips and advanced options

#### Baselines, failures and comparison scope

Use one Monitor ID per stable set of stores, platform and currency settings in the same Apify account. Tasks in the same account share state if they reuse the ID. Changes to store URLs, platform or currency require a new ID and a fresh baseline. Reusing an ID is how later runs find the previous observations; using a different ID starts over.

Rolling mode keeps the last successfully observed version of each product. A request failure, an unavailable store or a product missing from a partial scan does **not** change that product to out of stock. Missing products are reported separately, with no claim that they were deleted. When a product is seen again, it can be compared with its last good observation, which may be older than the immediately preceding run. Observed timestamps describe product fetches, not exact change times; an individual retained fact can come from an older observation.

Keep product limits and scheduling consistent. A small limit covers only part of a catalog; it is not a way to select specific product URLs. Inspect **REPORT** and **NOT\_OBSERVED** to see coverage. Each monitor retains at most 10,000 products, 1,000 historical variants per product and 10,000 pending notification events, subject to a combined 16 MB state limit. A capacity failure preserves the existing state; resolve delivery failures or use a smaller scope with a new Monitor ID. Observations delivered before such a failure can still be billed.

Run each Monitor ID sequentially. Allow the preceding run to finish before starting another, including manual retries. The storage check reduces accidental overlap but is not a guarantee of atomic locking across simultaneous starts. An interrupted run may leave a lease that expires after 15 minutes.

#### Connect changes to Make or n8n

1. Create a Make **Custom webhook** or an n8n **Webhook** endpoint that accepts JSON POST requests. Use a public HTTPS production endpoint you control.
2. Paste that endpoint into the Actor's optional **Change webhook URL** field in rolling mode. Apify stores this field as a secret input.
3. Establish the baseline, then run the same monitor again after a real source change, or use the example event below while building your receiver.
4. Map each received change into your chosen email or Slack action. Configure credentials and recipients in your own workflow.
5. Inspect **REPORT** after runs for notification results as well as catalog coverage. Deduplicate received events by `eventId`; do not assume every run generates a notification. Return a 2xx response when your receiver has accepted a batch.

The Actor posts `{ "schemaVersion": 1, "monitorId": "...", "events": [...] }`. Each event contains product identity, URL, current/prior timestamps and `changes` with `field`, `before`, `after` and `type`. Change types are `price_change`, `stockout` and `restock`. One product may generate several changes, and large products are split into events of at most 50 changes.

Delivery is bounded to 10 requests and 30 seconds per run, with at most 50 events and 200 KB per request. Failed or timed-out delivery remains queued for the next run with the same `eventId`. Notifications can be duplicated when a response is lost; delivery is not exactly once. Redirects are not followed. A successful HTTP response confirms endpoint acceptance, not final email or Slack delivery.

If events are pending, keep the same webhook URL until they are delivered. Changing or removing the URL is rejected to preserve the queued events; restore the original URL to retry or use a new Monitor ID. With no webhook configured, events are available in `CHANGES` but are not queued for future webhook delivery.

The payload is generic JSON, not the format expected by a direct Slack incoming webhook. Make/n8n and notification services have their own account requirements and usage limits. The Actor does not provision or subscribe you to them.

Notifications concern confirmed price changes and known stock transitions. A first observation, an unchanged product, an unknown stock value or an unverified-currency price string is not a confirmed price/stock event. Other catalog differences remain visible in each row's `changes` array.

#### Example webhook event

This complete JSON payload was generated from the controlled example product above. It is **test data**, not a real competitor change. Use it to build your receiver; the `eventId` is for this example only.

```json
{
  "schemaVersion": 1,
  "monitorId": "example-competitors",
  "events": [
    {
      "type": "product_changed",
      "monitorId": "example-competitors",
      "productKey": "shopify|https://example.com|5",
      "productId": "5",
      "title": "Example Blue Hat",
      "url": "https://example.com/products/blue-hat",
      "storeUrl": "https://example.com",
      "currency": "USD",
      "observedAt": "2026-09-22T09:00:00+00:00",
      "previousObservedAt": "2026-09-21T09:00:00+00:00",
      "changes": [
        {
          "field": "inStock",
          "before": true,
          "after": false,
          "type": "stockout"
        },
        {
          "field": "priceMin",
          "before": "10.5",
          "after": "12.5",
          "type": "price_change"
        },
        {
          "field": "priceMax",
          "before": "10.5",
          "after": "12.5",
          "type": "price_change"
        },
        {
          "field": "variants[2].inStock",
          "before": true,
          "after": false,
          "type": "stockout"
        },
        {
          "field": "variants[2].price",
          "before": "10.5",
          "after": "12.5",
          "type": "price_change"
        }
      ],
      "eventId": "d33e6cc018e108b96012b4232a426831b9a4af46dc61db82ed62389965378ba5"
    }
  ]
}
```

#### Manual snapshots and comparison

Existing integrations remain in manual mode unless they explicitly select rolling mode. For a first snapshot, omit `previousDatasetId`. For comparison against a particular run, supply that run's accessible dataset ID:

```json
{
  "storeUrls": ["https://rootree.ca/"],
  "platform": "woocommerce",
  "monitoringMode": "manual",
  "previousDatasetId": "YOUR_PREVIOUS_DATASET_ID",
  "maxProducts": 100,
  "maxProductsPerStore": 100,
  "maxSeconds": 240
}
```

Replace the placeholder with one previous run's dataset from this Actor. A scheduled manual run with a fixed dataset always compares against that fixed dataset. Choose rolling mode for automatic baseline updates.

#### Currency, variants and availability

Shopify's public catalog does not reliably declare currency. Leave `shopifyCurrency` blank unless independently confirmed. Raw price strings remain in the output, but differences with unknown or changed currency receive an `Observed` field suffix and `currency_unverified` or `currency_changed` context. They do not generate numerical price alerts. Batch stores with different currencies separately.

WooCommerce declares currency and minor units, which the Actor uses to normalize prices. Its parent catalog generally does not provide individual variation prices, SKUs or stock, so those values remain unknown. Up to 250 variants are included per product; `variantCount` and `variantsTruncated` expose the limit.

Catalog stock status does not guarantee checkout availability. This Actor does not access secret inventory, customers or sales volumes, and does not calculate shipping, checkout taxes or geographic discounts.

### FAQ, limitations and support

Private catalogs, headless storefronts, incompatible plugins, robots exclusions and access challenges may prevent collection. The Actor respects robots rules and stops on access restrictions; it does not use access circumvention or JavaScript rendering. Pagination, product caps, time limits, spending limits and response-size limits may produce partial scans. A catalog can change while it is being read, so even a completed scan is not an atomic snapshot.

Scheduling automates repeated invocation, but store changes and failed integrations may require attention. Check coverage and delivery reports periodically. No uninterrupted monitoring or notification delivery is guaranteed.

AI-agent callers can use the same structured inputs and outputs. A one-off purchase or x402 invocation does not itself establish a recurring monitor: recurring use needs the caller's persistent storage context and scheduling. This Actor does not claim x402 eligibility or guaranteed agent discovery.

Use the [Issues tab](https://apify.com/saahir-job-data/shopify-woocommerce-product-monitor/issues) to report a compatibility problem. Include the store root URL, run ID and report reason; do not post tokens or webhook URLs.

# Actor input Schema

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

1–50 store root URLs. Use the WordPress installation root for WooCommerce subdirectory stores. No product, collection or API URLs.

## `platform` (type: `string`):

Auto tries public Shopify catalog, then WooCommerce only if the first endpoint is absent or unsupported. Access blocks stop that store.

## `monitoringMode` (type: `string`):

Manual preserves one-off scraping and optional previousDatasetId comparison. Rolling keeps the last successful product observations for your monitorId automatically. Rolling needs persistent Apify storage; run the same saved task on your chosen schedule.

## `monitorId` (type: `string`):

Choose a stable unique name such as my-shopify-competitors. Required only for rolling mode. Keep the same ID and store/currency settings between runs. Use a new ID to start a fresh baseline; never run the same monitor concurrently.

## `webhookUrl` (type: `string`):

Optional public HTTPS webhook endpoint you control, such as a Make custom webhook or n8n Webhook node. Receives JSON for confirmed price/stock changes; first baseline and unchanged products produce no change alert. Configure downstream email or Slack delivery in your workflow. Not a direct Slack incoming-webhook payload.

## `previousDatasetId` (type: `string`):

Manual mode only. Select one prior run of THIS Actor to compare products, up to 10,000 rows / 12 MB. Leave empty for a baseline. Rolling mode manages its own baseline and rejects this field.

## `shopifyCurrency` (type: `string`):

Three-letter currency code such as USD. Applies to all Shopify stores in this run. Leave blank if unknown: raw prices remain visible but currency and numeric changes are not assumed. WooCommerce declares its currency automatically.

## `maxProducts` (type: `integer`):

Total delivered product observations across all stores. Includes unchanged products. At $3/1,000, 500 products cost $1.50 plus $0.001 start.

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

Keeps a large first store from consuming the whole run. Reaching a cap marks the scan incomplete.

## `maxSeconds` (type: `integer`):

Catalog processing budget only. Set the Apify run timeout higher to allow baseline reads, exports, persistence and optional webhook delivery. Store requests are sequential and rate limited.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.lttstore.com/"
  ],
  "platform": "auto",
  "monitoringMode": "manual",
  "maxProducts": 500,
  "maxProductsPerStore": 100,
  "maxSeconds": 240
}
```

# Actor output Schema

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

No description

## `csv` (type: `string`):

No description

## `report` (type: `string`):

No description

## `missing` (type: `string`):

No description

## `unresolved` (type: `string`):

No description

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

No description

## `monitorStatus` (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 = {
    "storeUrls": [
        "https://www.lttstore.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("saahir-job-data/shopify-woocommerce-product-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://www.lttstore.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("saahir-job-data/shopify-woocommerce-product-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://www.lttstore.com/"
  ]
}' |
apify call saahir-job-data/shopify-woocommerce-product-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,saahir-job-data/shopify-woocommerce-product-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/oaHjonWPTYdLzADLx/builds/K6lHSOg5g6gb1uWxP/openapi.json
