Shopify Price & Stock Change Monitor
Pricing
$5.00 / 1,000 change detecteds
Shopify Price & Stock Change Monitor
Reports only what changed in public Shopify catalogs since the last run: new and removed products, price changes and stock changes.
Pricing
$5.00 / 1,000 change detecteds
Rating
0.0
(0)
Developer
Tony Ojeda
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Watch public Shopify stores and get only what changed since your last run: new products, removed products, price changes (with old price, new price and percent change) and stock changes (in stock to sold out, and back). No giant catalog dumps to diff yourself.
Who it is for
- Ecommerce teams tracking competitor pricing and stock-outs
- Resellers and deal watchers who want an alert when a price drops or an item returns to stock
- Analysts building price histories without re-processing full catalogs every day
How it works
- You give it a list of public Shopify store URLs.
- For each store it reads
robots.txt, then the store's public/products.jsoncatalog, politely (one request per second per store at most). - Before your first run, pick a State storage in the input (a key-value store in your own account; create one under Storage if you have none) and use the same one every time. Apify runs Actors from the Store with limited permissions, so this is how the Actor remembers each catalog between runs. Without it every run starts a new baseline and reports no changes.
- The first run for a store only saves a baseline and outputs one
summaryitem with statusbaseline_saved. No change items are produced. - Every later run compares the fresh catalog with the saved one and outputs one dataset item per change, plus one
summaryitem per store.
Schedule it daily or hourly with Apify Schedules and connect the dataset to a webhook, Zapier, Make, Google Sheets or your own pipeline.
The two stores prefilled in the input are only a default test input. They are well-known public Shopify stores that this Actor's author does not control and is not affiliated with. Replace them with the stores you want to monitor, and make sure you are allowed to.
Input
{"storeUrls": ["https://www.allbirds.com", "https://www.brooklinen.com"],"maxStores": 25,"maxProductsPerStore": 5000,"maxPagesPerStore": 20,"minDelaySeconds": 1,"maxChangesPerStore": 1000,"maxCatalogShrinkPercent": 50,"stateStoreName": "shopify-price-stock-monitor-state"}
| Field | Default | Meaning |
|---|---|---|
storeUrls | two example stores | Public Shopify stores, https:// only, canonical address (redirects are not followed). |
maxStores | 25 | Stores beyond this are skipped with a note. |
maxProductsPerStore | 5000 | Larger catalogs are reported as too large, never partially monitored. |
maxPagesPerStore | 20 | Pages of up to 250 products. |
minDelaySeconds | 1 | Pause between requests to one store. Cannot be lower than 1. |
maxChangesPerStore | 1000 | Safety cap on billable items per store per run (for example after a store-wide sale). Price and stock changes are emitted before new and removed products. Changes beyond the cap are held back, not lost: they are reported on the following runs. |
maxCatalogShrinkPercent | 50 | Glitch guard. If a store's catalog is suddenly smaller than last time by more than this (for example an empty or maintenance response), nothing is reported and the old data is kept; it is accepted only if the next run sees about the same size (within 10%). 100 turns the guard off. |
stateStore | none | Pick a key-value store here (create one under Storage if you have none) and keep it the same for every run and schedule. It remembers each store's last snapshot so later runs report only what changed. Apify runs Actors from other users with limited permissions, which cannot open storage in your account unless you pick it here. Without it the Actor still runs, but every run saves a fresh baseline and reports no changes (a notice item says so). |
stateStoreName | shopify-price-stock-monitor-state | Used only when stateStore is empty and the run is allowed to open a named store (the Actor's owner, or a full-permission run). Lowercase letters, digits and hyphens. Change it to start over with fresh baselines. |
Output
Every dataset item has a type. These four are change items (billable):
{"type": "price_change", "store": "https://www.example-store.com", "observed_at": "2026-01-01T12:00:00+00:00","product_id": 1001, "handle": "wool-runner", "title": "Wool Runner","variant_id": 5001, "variant_title": "M8", "sku": "SKU-5001","old_price": "98.00", "new_price": "88.20", "percent_change": -10.0, "compare_at_price": null}
{"type": "availability_change", "store": "https://www.example-store.com", "observed_at": "2026-01-01T12:00:00+00:00","product_id": 1001, "handle": "wool-runner", "title": "Wool Runner","variant_id": 5002, "variant_title": "M9", "sku": "SKU-5002","old_available": true, "new_available": false, "change": "sold_out"}
new_product and removed_product carry the product's id, handle, title, vendor, product_type, updated_at and its variants (id, title, sku, price, compare_at_price, available). Removed products show their last known data.
These are not billable:
{"type": "summary", "store": "https://www.example-store.com", "observed_at": "2026-01-01T12:00:00+00:00","status": "baseline_saved", "products_seen": 412, "pages_fetched": 2, "malformed_products_skipped": 0,"changes_found": 0, "changes_emitted": 0, "changes_suppressed": 0, "changes_undelivered": 0,"counts": {"new_product": 0, "removed_product": 0, "price_change": 0, "availability_change": 0}}
status is baseline_saved on a store's first run and checked afterwards. If the run has no storage that survives between runs (no State storage picked), status is not_monitoring, the first dataset item is a notice with code state_not_persisted, and the run's status message starts with NOT MONITORING: no changes can be detected until you pick a State storage. changes_emitted is exactly the number of change items delivered (and billed) for that store; changes_suppressed counts changes held back by maxChangesPerStore; changes_undelivered counts changes that could not be delivered (spending limit reached, billing could not be verified, or a dataset error). Held-back and undelivered changes are re-detected and reported on the next run, and the summary note says so. A skipped item (for example the store's robots.txt disallows the catalog) or an error item (invalid URL, not a Shopify store, store unreachable, ...) carries a code and a plain message. One failing store never fails the run.
Prices are the strings Shopify publishes, in the store's own currency (the catalog does not state the currency). Nothing else is collected: no images, no descriptions, no customer or order data.
Pricing
Pay per event:
- Change detected: one billable event per change item (a new product, a removed product, a price change on one variant, or a stock change on one variant).
- Summaries, skipped stores, errors and baselines: not billable.
A run with no changes produces no per-change charges. Apify may still apply its own standard platform charges for a run (for example a start event on pay-per-event Actors); check the pricing shown on the Actor page. Use maxChangesPerStore and your Apify spending limit to cap a worst case.
Billing and delivery happen together: a change item is pushed to the dataset only when the platform confirms the charge. If your spending limit is reached mid-run, changes that could not be charged are held back and delivered on the next run after you raise the limit. If the platform returns no charge information (for example the Actor is run outside pay-per-event pricing), the Actor treats billing as unverified: it stops emitting change items for that run, says billing_unverified in the run status message, and keeps the changes for the next run (at most one item that revealed the problem may already be in the dataset and will be reported again). A product that is delisted and later relisted produces two billed items (removed, then new).
Limitations
- Only public stores that expose
/products.json. Password-protected stores, stores that disable the catalog, and non-Shopify sites are reported as errors. - Catalogs over 5,000 products (configurable up to 20,000), over 250 variants per product or over 50,000 variants in total are not monitored (reported as too large); a partial catalog would create false "removed" items.
- A sudden large shrink (an empty or maintenance response) is treated as a glitch: reported as an error item, nothing is billed, and the old data is kept. The smaller catalog is accepted only if the next run sees about the same size (within 10%); a different size starts the check over. There is no matching guard for the very first run or for sudden growth: take your baseline on a healthy day. A first baseline that is empty, or has exactly a multiple of 250 products or the product cap, is flagged in the summary note because it may be incomplete.
- Each store has a time budget (5 minutes) and each run has one (50 minutes); stores that do not fit are reported as errors or skipped and can be run again.
- Shopify can show different prices by visitor country (Shopify Markets). The Actor runs from Apify data centers without a country setting, so if a store prices by region you may see price changes caused by where the run happened, not by the store. Check a few results before relying on bulk price alerts.
- Do not run two overlapping runs against the same state storage; both would report and bill the same changes.
- Interrupted runs: before delivering, the Actor saves the changes it is about to deliver as a pending list in the saved state, and the next run delivers pending changes first. This means a crash cannot lose changes, but items delivered shortly before the crash may be delivered (and billed) again. If the saved state cannot be updated after delivery, the store summary says so.
- Spreadsheet formula injection: product titles, SKUs and vendors are store-controlled text and are passed through unchanged (only control characters are removed and length is limited). This Actor does not neutralize values that start with
=,+,-or@. If you export the dataset to CSV or Excel, neutralize or review such cells yourself before opening the file. - Redirects are not followed. Enter the store's canonical address (for example the
wwwform if the bare domain redirects). - Only the four change types are reported. New or removed variants of an existing product are not.
- Changes are detected between runs, not in real time. If a price moves twice between runs you see the net change.
- Prices carry no currency code and can vary by market or logged-in customer; you see what the public catalog shows.
Responsible use
- Only public catalog data is read: one
robots.txtrequest and the public/products.jsonpages. No other URLs are requested. - It respects
robots.txt(RFC 9309): if a store disallows/products.json(or any catalog page) for crawlers it is skipped and the reason is shown; a robots.txt server error (5xx) is treated as "disallow all"; a missing robots.txt (4xx) as "allowed". The most specific user-agent group wins. - It is rate limited (at most one request per second per store), backs off on errors and honors
Retry-After. It identifies itself in its User-Agent. Every request has a hard total time limit and size limit, and connections go only to public internet addresses. - Site owners can block it with a
User-agent: StudioOjedaShopifyMonitor/Disallow: /rule in robots.txt; see https://studioojeda.com/bots. - No personal data is collected.
- You are responsible for complying with each store's terms of service and with applicable law (including competition, database and data protection rules in your jurisdiction). Do not use this Actor against a store that has asked you not to.
- This Actor is not affiliated with, endorsed by or sponsored by Shopify Inc. "Shopify" is a trademark of its owner and is used only to describe what the Actor reads.
Contact
Bot information for site owners (including how to block this crawler in robots.txt with the token StudioOjedaShopifyMonitor): https://studioojeda.com/bots. Contact: bots@studioojeda.com.