Competitor Promotions & New Products Monitor
Pricing
from $1.60 / 1,000 promotion or launches
Competitor Promotions & New Products Monitor
Competitor promotions monitor: sale banners, discount codes, free-shipping thresholds, markdowns and new products or collections on competitor stores. Shopify feeds plus any store's pages; a promotion ends only after two runs without it. Changes-only watchlists, compact JSON for AI agents.
Pricing
from $1.60 / 1,000 promotion or launches
Rating
0.0
(0)
Developer
CleanPull
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Features
Share
This competitor promotions monitor reads competitor store addresses and gets every promotion they show: sale banners, discount codes, free-shipping thresholds, bundles, gifts, sign-up offers and markdowns, plus new products and collections with the date each went live. It reads Shopify stores' public product and collection feeds and the pages of any store (WooCommerce, Magento, Salesforce Commerce Cloud, PrestaShop, BigCommerce and custom). Schedule it and every run says what started, what ended and what launched since the last one. A promotion counts as ended only after two runs in a row without it, so rotating banners are never reported as ended. It runs once, on a schedule or through the API, with no sign-in.
Also known as a competitor sales tracker, discount code monitor, promo tracker, free-shipping threshold checker and new product launch tracker.
How to monitor competitor promotions
- Open Competitor Promotions & New Products Monitor on Apify and go to its input form.
- Enter competitor store addresses in Stores, one per line (brand.com, or brand.com/us/ for a country site). Add a sale page, a new-arrivals page or a product page in Extra pages to read those too.
- Pick which kinds to report in Promotions to report, and keep New products and collections on for launches; on Shopify, Launch window on the first run (days) sets how far back the first run looks.
- Start the run. Open the Promotions view to read each promotion's Type, Status, Promotion, Code, Threshold and % off, and the Launches view for each new product's Title, Launched and Price.
- To be told what changes, turn on Changes only, give the watchlist a Watchlist name, save it as a task and schedule it daily. The Changes (one row per store) view then lists what Started, what Ended and the Launches since the last run.
What the promotions monitor gives you
- "Tell me when a competitor starts or ends a sale." Put their stores in a watchlist, turn on Changes only and schedule it daily: you get the new promotions, the ones that ended (with their last sighting) and one change summary per store.
- "What discount codes and free-shipping thresholds are my competitors showing?" Every code printed on the site, and every free-shipping offer with its amount and currency ($35+, over £50, dès 60 €).
- "How deep are their markdowns?" On Shopify, the share of the newest products priced below their compare-at price and the deepest cuts; elsewhere, the crossed-out prices on a sale page.
- "What did they launch this week?" Shopify products and collections with their publish date; on other stores, product links new on the pages you watch.
- "Feed a digest or an AI agent." Compact JSON with the same fields for every store, a field picker, and a dataset view per job.
How the promotions monitor reads a store
- The home page (or the country site you give,
brand.com/us/): its announcement bar, header, hero banners (including their image alt text) and the rest of the page. Menus, footers and pop-ups count only when a text names a code, a threshold or a discount. - Shopify stores: the store's public feeds,
/products.json(the newest published products, with prices and compare-at prices) and/collections.json. - Extra pages you add: a sale page (its crossed-out prices are a markdown), a new-arrivals page (new product links are launches), a product page (its structured sale price is a markdown).
Each promotion has a confidence: high in an announcement bar, the header or the store's own data; medium elsewhere on the page, in menus or in image alt text.
In tests on 26 stores on 9 platforms, read from Apify's servers, it read 26 of 26. It found an announcement-bar promotion on 13, a free-shipping threshold on 16 and a discount code on 7, and launches with dates on all 7 Shopify stores that publish their feed. Read twice, minutes apart, 27 pages showed no false changes. One rotating bar (Old Navy) swapped a promotion between runs, and nothing was reported as ended.
Promotions monitor accuracy
- Ended means ended. A promotion missing once is only counted in the run summary; it is reported as ended when the next run misses it too. A page that fails to load never counts as the promotion missing.
- One promotion stays one promotion. A code is the promotion whatever words surround it; a free-shipping offer is its threshold; countdowns and dates in a banner ("ends in 2h 13m") do not make a new one.
- Not every "sale" is a promotion. Menu links to sale sections, "Sale price" lines on product cards, cart messages ("spend $20 more for free shipping") and privacy text are left out. Codes are read only as stores print them, in capitals after "code".
- A block is never an empty result. Each store gets its own proxy ladder (direct, then datacenter, then residential only for stores that block the others); a store that blocks every tier is named in the run summary, with the reason, and not charged.
- Launches are real. On Shopify, a product counts as launched when it is new to the watchlist and went live since the last run, dated by the store. On other stores, the first read of a page records its product links as a baseline, and a new link becomes a launch only when two runs in a row show it, so a rotating carousel never reads as a launch.
- Only delivered rows and page checks are billed. Each page read is charged as a page check, with or without Changes only, and each delivered promotion or launch as a row (see Pricing below). Pages that could not be read cost nothing; the change summary is free; a run stopped by a limit is checked in full next time, and rows already delivered are never charged twice, even after a restart.
- Only public addresses are read. A URL or redirect pointing into a private network is never fetched.
Promotions monitor input
| Field | Key | What it does |
|---|---|---|
| Stores | stores | Store addresses, one per line (brand.com, or brand.com/us/ for a country site) |
| Extra pages | pages | Optional. Other pages of those stores: a sale page, new arrivals, a product page (up to 10 per store; each is one page check) |
| Promotions to report | promotionTypes | Which promotions to report and remember: sitewide-sale, sale, code, free-shipping, bundle, gift, signup-offer, markdown (all by default) |
| New products and collections | includeLaunches | New products and (Shopify) new collections (on by default) |
| Shopify collections | includeCollections | Also read Shopify's collections feed for new collections (on by default) |
| Launch window on the first run (days) | launchWindowDays | First run: Shopify launches within this many days (default 7) |
| Shopify products read per store | maxProductsPerStore | How many of a Shopify store's newest products to read (default 100); launches and the markdown share come from these |
| Launches per store per run | maxLaunchesPerStore | The most launch rows one store gives in a run (default 25); the rest come on the next run |
| Changes only | changesOnly | Only what changed since the last run with the same watchlist name, plus one change summary per store |
| Watchlist name | monitorName | Runs with the same name compare against each other (default default) |
| Max cost per run (USD) | maxRunCostUsd | Optional. Stop once this run's charges would pass this amount; for scheduled runs, which may have no spending limit of their own. Stores not finished are checked in full on the next run |
| Only these fields | fields | Keep only these fields, for example ["store", "type", "status", "text", "code", "threshold"] |
| Stores at a time | maxConcurrency | Stores read in parallel (default 3); each store gets at most two requests at a time |
| Proxy | proxyConfiguration | Optional. The actor picks proxies itself per store: direct, then datacenter, then residential only for stores that block the others |
| Start URLs | startUrls | Store addresses, same as Stores |
| History store | stateStoreName | Optional key-value store for what earlier runs saw (default cleanpull-promotions-monitor); pick one when running with a scoped token in Restricted access mode |
Example inputs
A competitor watchlist, checked daily for changes:
{"stores": ["https://www.tentree.com", "https://www.zwilling.com/us/", "colourpop.com"],"changesOnly": true,"monitorName": "competitors"}
A store and its sale page, codes, free shipping and markdowns only:
{"stores": ["https://www.zwilling.com/us/"],"pages": ["https://www.zwilling.com/us/sale/"],"promotionTypes": ["code", "free-shipping", "markdown"]}
What Shopify stores launched in the last 30 days:
{"stores": ["kith.com", "colourpop.com"],"launchWindowDays": 30,"promotionTypes": [],"maxLaunchesPerStore": 200}
Promotions monitor output
Three kinds of rows, told apart by recordType.
Example rows
A real promotion row:
{"recordType": "promotion","store": "tentree.com","storeUrl": "https://www.tentree.com/","platform": "shopify","promotionId": "015303d778fcb51bef4535a04dd3d6a40b05da1774f82bc88ecdcb755008149e","type": "code","status": "running","text": "FREE SHIPPING ENDS MONDAY. CODE: FREESHIP","code": "FREESHIP","threshold": null,"thresholdCurrency": null,"percentOff": null,"amountOff": null,"confidence": "high","foundIn": "bar","pageUrl": "https://www.tentree.com/","firstSeen": "2026-10-14T23:19:26.522Z","lastSeen": "2026-10-14T23:19:26.522Z","endedAt": null,"firstRun": true,"checkedAt": "2026-10-14T23:19:26.522Z"}
status is new (first seen since the last run), running (seen again, or on a watchlist's first run, which also has "firstRun": true) or ended (missing two runs in a row; lastSeen is its last sighting). Free-shipping rows carry threshold and thresholdCurrency (from €, £ or an ISO code in the text, else the store's own currency when the symbol fits it; a bare $ on a store with no stated currency stays null); markdown rows carry markdown (on Shopify: products read, discounted, sharePercent, maxPercentOff and the ten deepest cuts with their prices).
A launch row:
{"recordType": "launch","store": "kith.com","platform": "shopify","kind": "product","title": "Giorgio Armani & Kith for ASICS GEL-MAI 0.1 - Monument / Quarry","url": "https://kith.com/products/ai1203b454-400-dr","launchedAt": "2026-10-14T13:52:49-04:00","createdAt": "2026-10-14T13:52:46-04:00","relaunch": false,"confidence": "high","source": "shopify-feed","price": 300,"compareAtPrice": null,"currency": "USD","vendor": "Kith","checkedAt": "2026-10-14T23:19:21.978Z"}
kind is product or collection; source is shopify-feed (dated by the store) or page-diff (a product link shown on two runs in a row, dated when first seen). relaunch marks a product published more than 30 days after it was created.
Change rows and run summary
From the second run on, each store with something new also gets one free change row: started and ended promotions, launches with their titles, freeShippingThreshold when it moved, and markdownSharePercent when the markdown share moved by 10 points or more.
Dataset views: Promotions, Launches and Changes (one row per store). RUN_SUMMARY in the key-value store gives the outcome (COMPLETE, PARTIAL, NO_CHANGES, EMPTY, FAILED), per store the platform, pages read, promotions seen, started, ended and missing once, launches and residential requests, the charges, and every page that could not be read, with the reason.
Pricing
| Event | Free and Bronze | Silver | Gold and above |
|---|---|---|---|
| Promotion or launch (a delivered row) | $2.00 | $1.80 | $1.60 |
| Page checked (a home page with its Shopify feeds, or an extra page) | $1.00 | $0.90 | $0.80 |
| Request through residential addresses (add-on, only for stores that block the others) | $6.00 | $6.00 | $6.00 |
Per 1,000. Each run also has a $0.00005 start fee. The change summary rows are free. A watchlist of 20 competitor stores checked daily with Changes only is about 600 page checks a month ($0.60 on the Free and Bronze plans), plus the promotions and launches that changed. Requests through residential addresses are capped at 500 KB each (a larger one is not read or charged).
Limits
- Bars rendered only by scripts are not read (the actor reads the page's HTML, without a browser): Kith's and Nike's, for example.
- Codes are read when the site prints them; codes sent by email, behind a pop-up click or in a cart are not.
- Launches on stores without a Shopify feed come from product-shaped links (
/products/,/product/,/p/, numbered.htmlpages); stores whose product addresses look like categories give no launches. - Shopify feeds are read only where the store publishes them (headless fronts often do not: their launches come from product links).
- Prices and promotions are those a visitor from the proxy's country sees, without a sign-in.
- Stores that block every proxy tier (Uniqlo, Crocs, Adidas in tests) are listed in the run summary and not charged; Amazon, Walmart and Target are not read.
- Shopify's bot check: when a Shopify store's home page is behind its bot check, the banners are read from its cart page (or shipping policy page), which show the same announcement bar and header. Read that way, only site-wide banners can end; offers shown only on the home page wait until it can be read again. When every one of those pages is checked too, the store gets a free row saying its promotions couldn't be read this time, and is checked again on the next run.
- Read stores at a pace their terms allow: at most two requests at a time per store.
- Nothing found: when nothing could be read because it is not there (a store whose home page is not there, or that redirects in a loop, or input naming no store), the run still succeeds, with one free
recordType: "unreadable"row per item giving thereason; nothing is charged. A run near its time limit stops starting stores, delivers what it read and lists the rest inRUN_SUMMARY(as free rows when it read no store). These free rows come only when no store was read; otherwise the misses are listed inRUN_SUMMARY, and a Shopify store behind its bot check still gets its free row. If no store was read and any store was blocked on every proxy tier or failed with an error, the run ends FAILED with the reasons inRUN_SUMMARY, no free rows are given, even for the misses, and nothing is charged.
FAQ
How much does competitor promotion monitoring cost per store per day?
One store checked once a day with Changes only costs $0.001 a day on the Free and Bronze plans, plus $0.002 for each promotion that started or ended and each launch; 20 stores cost $0.02 a day (about $0.60 a month) before those rows. Without Changes only, every run delivers each promotion and launch it finds, so a store showing 3 promotions costs $0.007 a run. Each page in Extra pages adds $0.001 a run, and a store that blocks standard addresses adds $0.006 for each request read through residential addresses. Pages that could not be read, stores that blocked every tier and the change summary rows are never charged. Each run also has the start fee, and higher Apify plans pay less (see Pricing below).
How often should I check competitor promotions?
Daily suits most watchlists. With Changes only and a schedule, each run returns only new promotions, promotions that ended and launches since the last run under the same Watchlist name, plus one change summary per store. A promotion is reported as ended only after two runs in a row without it, so a daily schedule confirms an end within two days, and rotating banners are not reported as ended. Max cost per run (USD) caps what a scheduled run can spend.
What is the difference between a sale, a sitewide sale and a markdown?
A sale or sitewide-sale promotion is an offer the store announces in its bar, header or banners, with percentOff or amountOff when the text gives one. A markdown is what the prices show: on Shopify, the share of the newest products priced below their compare-at price (markdown.sharePercent, markdown.maxPercentOff and the ten deepest cuts); elsewhere, the crossed-out prices on a sale page or the structured sale price on a product page you add in Extra pages. A code row carries the code as the store prints it, and a free-shipping row its threshold and thresholdCurrency.
Why is a promotion or launch missing?
- Rendered by scripts: bars drawn only by scripts are not read, since the actor reads the page's HTML without a browser.
- Code not printed: codes sent by email, behind a pop-up click or in a cart are not on the page.
- Left out on purpose: menu links to sale sections, "Sale price" lines on product cards, cart messages and privacy text are not promotions.
- No Shopify feed: on other stores, launches come from product-shaped links on the pages read, and a new link counts only when two runs in a row show it.
- Over the cap: a store gives at most Launches per store per run launch rows; the rest come on the next run.
- Store not read: a store that blocked every proxy tier is named in
RUN_SUMMARYwith the reason; a Shopify store behind its bot check on every page gets a freerecordType: "unreadable"row with thereason, and is checked again on the next run.
Which stores and platforms does it support?
Shopify stores (with their public product and collection feeds) and the pages of any other store, such as WooCommerce, Magento, Salesforce Commerce Cloud, PrestaShop, BigCommerce and custom stores, including country sites (brand.com/us/). Amazon, Walmart and Target are not read.
Can I use the promotions monitor through the API?
Yes. Start runs through the Apify API or an API client, and read the dataset through its Promotions, Launches and Changes (one row per store) views, or the RUN_SUMMARY record with the outcome, per-store counts, charges and every page that could not be read. Only these fields (fields) trims each row to the keys you need.
Does it work with Google Sheets, Make, Zapier or Claude?
Yes. Apify's integrations send each run's results to Google Sheets, Make, Zapier, webhooks and other tools, and a schedule keeps them updated. From Claude, the MarketMage plugin reads a competitor's sales, codes and free-shipping threshold, or watches them on a schedule, in a chat.
Is it allowed to monitor competitor promotions?
The actor reads public store pages and Shopify feeds as a signed-out visitor sees them: no login, no account and no customer data. How you use the data, and following the laws and terms that apply to you, is your responsibility. This is not legal advice.
Related actors
Other CleanPull actors for monitoring:
- Shopify Scraper: every product and variant from any Shopify store, with stock and sales estimates.
- Price Tracker & Stock Monitor for Any Online Store: price and stock changes on product pages of any online store.
- Reviews Monitor: new reviews and rating moves on Shopify review apps, Amazon and Trustpilot.
- Watch Digest: one scheduled digest of price, stock, rank and review changes.
Use it from Claude
The MarketMage plugin runs these actors from a Claude chat.
Found it useful? A rating on the Store helps others find it.
Changelog
- 0.1.x, moved stores (2026-11-09): a store whose address now redirects to another domain (a rebrand) is read on the new domain, following up to 3 moves, and the status message says where it was read; a redirect loop gets a free row. Prices and events unchanged.
- 0.1.x, Shopify bot check (2026-11-08): a Shopify store whose home page is behind its bot check is read through its cart or shipping policy page; when nothing can be read, the store gets a free row saying its promotions couldn't be read this time, and the run no longer fails. Messages never show a web address's query string or a challenge token. Prices and events unchanged.
- 0.1.x, reliability (2026-11-01): a store address that does not exist (no such domain) now ends with a free
unreadable'not found' row instead of a failed run; the address is checked before any proxy is used, so it costs nothing. Prices and events unchanged. - 0.1.x, reliability (2026-10-24): runs that find nothing (a store whose home page is not there or sends shoppers to another site, or input naming no store) end SUCCEEDED with a free
unreadablerow per item saying why, instead of FAILED; a run stops starting stores before its time limit and ends with what it read, never TIMED-OUT. A run where the site refused on every route for every store still fails. Prices and events unchanged. - 0.1.x (2026-10-14): RUN_SUMMARY
perStoresays whether each store was delivered in full (complete); a store whose page check could not be paid for no longer counts instoresRead. - 0.1 (October 2026): promotions from announcement bars, headers, banners and image alt text (sales, codes, free-shipping thresholds, bundles, gifts, sign-up offers); Shopify product and collection feeds for markdowns and dated launches; sale and product pages; launches from new product links; watchlists with changes only, ended after two runs; Max cost per run.