Get an Alert When a Competitor Changes Prices on Their WooCommerce Store
If you sell anything that other shops also sell, you've probably done this: open a few competitor tabs every Monday, scroll their catalogs, and try to remember what things cost last week. That works for 20 products. It breaks at 200.
This post covers a setup that does the checking for you: read a competitor's WooCommerce catalog from its own public API, diff it against the last snapshot on a schedule, and send only the changes — new products, removed products, price changes, stock flips — to Slack, email or a spreadsheet.
Step 0: the thing most people don't know about WooCommerce
Almost every WooCommerce store exposes a public, read-only Store API. It's what the block-based cart and checkout use, and it's on by default:
https://competitor-store.com/wp-json/wc/store/v1/products?per_page=100&page=1
Open that URL for any Woo store and you get clean JSON: product names, SKUs, prices
in minor units (1650 means $16.50), sale prices and stock status. No login,
no API key, no HTML parsing. That gives you two advantages over scraping product pages:
it doesn't break when the store changes themes, and it's light on the store — a
handful of paginated JSON requests instead of hundreds of page loads. (Private,
password-protected and B2B stores turn this API off; that's fine, those aren't public
prices anyway.)
Step 1: snapshot and diff on a schedule
Reading the API once is easy. The useful part is remembering last run's catalog and
reporting only what changed. That's what
WooCommerce Product
Feed & Catalog Monitor does: the first run for a store records a baseline and is
never charged for "changes", and every later run emits one dataset row per change, typed
new, removed, price_changed or
stock_changed.
{
"storeUrls": ["https://competitor-one.com", "https://competitor-two.com"],
"maxProductsPerStore": 1000,
"outputMode": "changes",
"stateName": "competitor-watch",
"maxChargeUsd": 2
}
A change row looks like this:
{
"recordType": "change",
"store": "https://competitor-one.com",
"changeType": "price_changed",
"name": "Ceramic Mug - 12oz",
"sku": "MUG-12",
"price": 1650,
"oldPrice": 1400,
"currency": "USD",
"stock_status": "instock",
"permalink": "https://competitor-one.com/product/ceramic-mug-12oz",
"detectedAt": "2026-10-16T09:00:00+00:00"
}
Keep stateName identical across runs — it's the key-value store
holding the snapshot. Only change it when you want a fresh baseline.
Schedule it
In Apify Console: Schedules → Create new, pick the actor (or save
the input above as a Task first, which we recommend), and set a cron expression —
0 7 * * * for daily at 07:00, or 0 */4 * * * for every four
hours on fast-moving categories.
What it costs
| Event | Price |
|---|---|
| Run started | $0.001 |
| Product checked | $0.0002 ($0.20 per 1,000) |
| Change detected | $0.005 |
A 1,000-product competitor checked daily with about 10 changes a day costs roughly
$0.25 a run. Lower maxProductsPerStore or the schedule frequency to spend
less. maxChargeUsd is a hard per-run cap, and stores that fail (no public
API, network errors) return an error row and aren't charged.
Step 2a: alerts with Zapier
- Trigger: Apify → Finished Task Run. Connect your Apify account and pick the task from step 1.
- Action: Apify → Fetch Dataset Items, using the
defaultDatasetIdfrom the trigger. - Filter: only continue if
recordTypeexactly matcheschangeandchangeTypeexactly matchesprice_changed(add a second path forstock_changedornewif you want those too). - Formatter → Numbers → Perform Math Operation: divide
priceandoldPriceby 100 to show dollars instead of cents. - Action: Slack → Send Channel Message with the formatted values
and the
permalink.
If a run returns many changes, Zapier sends one message per row by default; use Formatter → Utilities → Line-item to Text for a single digest message per run.
Step 2b: alerts with Make
- Apify → Watch Task Runs (instant trigger; Make creates the webhook for you).
- Apify → Get Dataset Items, with Dataset ID =
defaultDatasetIdfrom module 1. - Filter between modules 2 and 3:
recordType = changeandchangeType = price_changed. - Google Sheets → Add a Row (a price-history log) and/or Slack → Create a Message.
Add a Router with one route per changeType: send new
products to a "competitor launches" channel and stock_changed to an
out-of-stock channel. A competitor selling out is often the best moment to push your own
ads.
Step 2c: no automation tool at all
Apify has built-in integrations on every task — Slack, email, Google Drive, webhooks. Open the task, go to Integrations, and add Slack or Send Email triggered on Run Succeeded. You won't get per-row filtering, but you get a "the run finished, here's the dataset" notification for free.
The code version
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dheerajitbhu/woocommerce-catalog-monitor").call(run_input={
"storeUrls": ["https://competitor-one.com"],
"outputMode": "changes",
"stateName": "competitor-watch",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row.get("recordType") == "change" and row["changeType"] == "price_changed":
old, new = row["oldPrice"] / 100, row["price"] / 100
pct = (new - old) / old * 100 if old else 0
print(f"{row['name']}: {old:.2f} -> {new:.2f} ({pct:+.1f}%) {row['permalink']}")
A few honest caveats
- You only see public prices. Member-only pricing, coupons applied at checkout, and B2B price tables aren't in the Store API.
- Prices come back in the store's own currency and minor units; convert before comparing across currencies.
- Be a good citizen: the actor makes sequential requests with small delays. Daily or every few hours is plenty for price intelligence. Use the data in line with each store's terms.
Wrap-up
Read the Store API endpoint, keep a snapshot, diff it on a schedule, and route only the changes to where you'll actually see them. That turns "check competitor sites every Monday" into a Slack message the moment something actually moves. Try it: WooCommerce Product Feed & Catalog Monitor on Apify.