MAP Price Violation Monitor & Alerts
Pricing
from $2.00 / 1,000 baseline price snapshots
MAP Price Violation Monitor & Alerts
Monitor your product list across retailer sites and get alerted the moment a price drops below your MAP (minimum advertised price). Stateful: alerts only on new violations, deeper cuts and recoveries — never repeats. Works on Shopify, WooCommerce, BigCommerce, Magento and custom shops. GDPR-clean.
Pricing
from $2.00 / 1,000 baseline price snapshots
Rating
0.0
(0)
Developer
Lowland Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
Watch your product list across retailer websites and get an alert the moment a retailer advertises below your MAP (minimum advertised price) — plus the deeper cuts and the recoveries. The monitor remembers what it has already told you, so an alert always means something changed, never "still broken, billed again."
It reads prices the way Google does — from the shops' own structured data — so it works across thousands of independent web shops (Shopify, WooCommerce, BigCommerce, Magento, custom platforms) without per-site parsers. Output is strictly product and price data: no shop-owner names, no contact details, nothing personal — safe to store, share with your legal team, or feed into enforcement letters.
Quick start (30 seconds)
- Open the actor and press Start — the prefilled demo list (3 real products with demo MAP values) runs in a few seconds and shows you a baseline snapshot, including one MAP violation.
- Replace the demo list with your products: one
{ "url", "mapPrice" }pair per retailer product page (paste your price list into the AI prompt below if you don't want to write JSON). - Add a daily schedule (Actor page → Schedule). From then on, runs are quiet until a price actually crosses your MAP.
Who uses this
- A consumer-electronics brand with 40 authorized dealers lists each dealer's
product page for its 12 SKUs. Tuesday 9:00, one dealer starts a "-15%" promo that
goes under MAP on three SKUs — three alert items appear with the exact advertised
price and depth (
deltaPct: -14.97), ready to paste into the enforcement email. - A supplement brand watches 200 web-shop listings of its range. Most runs cost a fraction of a cent and stay empty; the run after a rogue discounter cut prices contains exactly the new violations, nothing it already reported yesterday.
- A sports-gear distributor enforces reseller agreements across independent dealer shops in three countries, one monitor per country (state stores keep them separate), each with the currency of that market — a euro price is never compared against a dollar MAP.
- An agency running MAP programs for client brands schedules one monitor per client and exports the violation history per quarter as evidence of enforcement.
What you get
One JSON item per event: a baseline snapshot per product on the first run, then only transitions — new violation, deeper cut, recovery, or a page that stopped being readable. A violation item looks like:
{"productUrl": "https://dealer-shop.example.com/products/pro-widget-2000","label": "Pro Widget 2000","retailer": "dealer-shop.example.com","productName": "Pro Widget 2000 — Stainless","status": "violation","isViolation": true,"isBaseline": false,"isUpdate": true,"advertisedPrice": 84.99,"priceHigh": null,"priceIsVariantLow": false,"currency": "USD","currencyVerified": true,"mapPrice": 99.95,"deltaAbs": -14.96,"deltaPct": -14.97,"availability": "in-stock","priceSource": "json-ld","checkedAt": "2026-07-16T06:30:12.000Z","note": null}
Field honesty notes:
advertisedPriceis what a shopper sees on the page now — on sale pages the sale price, never the crossed-out list price. For multi-variant products it is the cheapest advertised variant (priceIsVariantLow: true, range top inpriceHigh).currencyVerified: falsemeans the page did not state an unambiguous currency (e.g. a bare$) and your input currency was assumed. A page that clearly advertises a different currency is never compared — it becomes an unchargedcurrency-mismatchitem instead.priceSourcetells you which extraction path read the price;json-ld(the shops' own Google-facing structured data) is the most reliable.- When a page can't be read with confidence, you get
status: "unreadable"or"unreachable"— reported once per change, never charged, never guessed.
How much does MAP monitoring cost?
Pay per outcome — checking your whole catalog costs nothing; you pay when the monitor tells you something new:
- MAP violation alert (new violation or deeper cut): $0.10
- Price recovered alert (optional,
alertOn): $0.02 - Baseline snapshot item (first check of a product): $0.002
- Actor start: $0.0001. Platform usage (a few cents per run) is billed by Apify.
Cost feel: monitoring 100 products daily costs $0.20 once for the baseline, then roughly $0.50–2/month at typical violation rates — a quiet month is nearly free. Dedicated MAP software subscriptions for the same job run $50–500/month.
Why an alert costs more than a scraped row
The store's headline ("from $2.00 / 1,000 baseline price snapshots") is what a trial costs: 500 products baselined = $1. Apify displays every event per 1,000, so in the pricing table the violation alert reads "$100 / 1,000" next to price scrapers at $1–3 per 1,000 rows — but that comparison is between different things:
- A raw price scraper sells rows: re-download all 100 products every day (3,000 rows/month), then build your own diffing, state and MAP logic on top.
- A stateless checker re-flags every known violation every run — on day 30 you have paid 30 times for the same broken price, and your inbox filter, not the tool, decides what is new.
- This monitor sells the transition: it remembers every product's last known state and charges $0.10 exactly when a retailer newly crosses your MAP or cuts deeper. Nobody buys 1,000 alerts a month — if you did, you'd have bigger problems than tooling costs.
Not technical? Let your AI assistant set it up
Paste this into ChatGPT, Claude, or any assistant, together with your price list (a pasted spreadsheet, CSV, or even "product page links plus the minimum prices"):
Convert my price list into the JSON input for the Apify actor"MAP Price Violation Monitor & Alerts" (lowlanddata/map-price-violation-monitor)and tell me exactly where to paste it.Rules for the input:- The input field "products" is a JSON array; one object per retailer productpage: { "url": "<full product page URL>", "mapPrice": <number>,"currency": "<ISO code like USD or EUR, default USD>","label": "<short name I'll recognize>" }.- One entry per RETAILER PAGE: if 3 shops sell the same product, that is 3entries with the same label and mapPrice but different urls.- Other fields I might set: alertOn ("violations" or"violations-and-recoveries"), maxItems (default 500). Leave emitBaseline,resetState and stateStoreName at their defaults.- Then tell me how to add a daily schedule on Apify so I get alertsautomatically, and how to add an email notification when a run pushes items.Here is my price list:[PASTE YOUR LIST / SPREADSHEET COLUMNS HERE]
Coverage — measured, not promised
The extractor was validated against a live sample of real shops across the platform families (July 2026): the shops' own structured data (JSON-LD) reads a correct price on about 60% of long-tail shops; with the platform fallbacks (Shopify product JSON, price metas, WooCommerce core markup) coverage reached 14 of 15 sampled shops. What that means in practice:
- Works: independent and mid-size web shops — Shopify (incl. headless), WooCommerce, BigCommerce, Magento and custom storefronts that publish price structured data for Google (most do; it's how they get rich results).
- Not covered in v1 — by design, stated honestly:
- Amazon, Walmart, Home Depot and similar marketplaces/big-box sites. They publish no structured price data and/or actively block automated access. We don't play cat-and-mouse with bot walls.
- Shops that render prices only in JavaScript (no structured data in the
HTML). These produce
unreadableitems — uncharged. - Login-walled or challenge-walled pages —
unreachable, uncharged.
- A page the monitor cannot read with confidence is never guessed and never charged — precision beats recall when every alert is a billed event.
- Regional pricing: many shops price by visitor location. Set the proxy country (Input → Proxy → country) to the market whose advertised price you are enforcing, e.g. US for a USD MAP list.
How the monitor thinks
- First run = baseline. Every product is checked and pushed once as
isBaseline: true(violating or not) at the cheap baseline price, seeding the monitor's memory. A first-sight product whose page could not be read still gets its baseline row — uncharged, like every problem item. TurnemitBaselineoff to seed silently. - After that, only transitions. An unchanged violation never re-alerts. A violation alerts again only when the price cuts deeper. A price that rises but stays below MAP stays silent (still a violation, not news).
- Recoveries (violating → back at/above MAP) alert only with
alertOn: "violations-and-recoveries", at the lower recovery price. - Editing the list is safe: adding a product baselines only that product;
removing one just stops checking it. Changing a product's
mapPricere-evaluates it against the new MAP — if the standing price is below the new MAP, that alerts as a new violation. - Memory lives in a named key-value store (
stateStoreName, defaultmap-monitor-state) in your account. One monitor per store: give each brand, market or client its own store name.resetState: truewipes it for a fresh baseline. - Re-running after a crash or overlap re-alerts at most the last few unsaved items — state is checkpointed during the run.
Input reference
| Field | Type | Default | Notes |
|---|---|---|---|
products | array | 3 demo products | 1–500 entries: url, mapPrice, optional currency (ISO, default USD), optional label. |
alertOn | enum | violations | violations or violations-and-recoveries. |
emitBaseline | boolean | true | Push first-sight snapshots; off = silent seeding. |
resetState | boolean | false | Forget everything; next run is a full baseline. |
maxItems | integer | 500 | Per-run push cap. Alerts are pushed before baselines; overflow alerts on the next run. |
stateStoreName | string | map-monitor-state | Key-value store holding monitor memory; one per independent monitor. |
proxyConfiguration | object | Apify datacenter | Set a country to read region-specific prices. |
Duplicate URLs are checked once (first entry wins). ~1 page fetch per product, politely paced per shop: 100 products finish in well under a minute.
Scheduling and API use
The monitor is built to run on a schedule (daily is typical; hourly for launch weeks). In Console: Actor page → Schedules. Get alerted only when something happened: Actor page → Monitoring → notify when dataset items > 0.
Run it from code and get the items back in one call:
curl -X POST \"https://api.apify.com/v2/acts/lowlanddata~map-price-violation-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"products": [{"url": "https://dealer-shop.example.com/products/pro-widget-2000", "mapPrice": 99.95, "label": "Pro Widget 2000"}]}'
// Node.jsimport { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('lowlanddata/map-price-violation-monitor').call({products: [{url: 'https://dealer-shop.example.com/products/pro-widget-2000',mapPrice: 99.95,label: 'Pro Widget 2000',},],});const { items } = await client.dataset(run.defaultDatasetId).listItems();
# Pythonfrom apify_client import ApifyClientclient = ApifyClient(token=os.environ["APIFY_TOKEN"])run = client.actor("lowlanddata/map-price-violation-monitor").call(run_input={"products": [{"url": "https://dealer-shop.example.com/products/pro-widget-2000","mapPrice": 99.95, "label": "Pro Widget 2000"}]})items = client.dataset(run["defaultDatasetId"]).list_items().items
Keep the product list under ~300 for synchronous calls (the sync endpoint has a 300-second wall); larger lists run fine asynchronously.
Use it with AI agents (MCP)
Claude, Cursor and other MCP-capable agents can run this monitor as a tool through Apify's hosted MCP server: the agent builds the product list, starts the run and reads the violations — no glue code.
Claude Code:
$claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/map-price-violation-monitor"
Cursor or Claude Desktop (add a custom connector / MCP server with this URL):
https://mcp.apify.com?actors=lowlanddata/map-price-violation-monitor
Sign in with your Apify account when prompted — runs are billed to it. Setup details per client: Apify MCP docs, or start from this actor's own MCP page: apify.com/lowlanddata/map-price-violation-monitor/api/mcp.
Prompts that work once connected:
- "Here is my price list as a spreadsheet — turn it into the monitor's product JSON and run a baseline."
- "Check my MAP list and draft an enforcement email for every new violation, including the price evidence."
- "Which of my retailers recovered to MAP this week?"
Is it legal to monitor retailer prices?
Yes — advertised prices on public product pages are public commercial
information; reading them is what every shopper, comparison engine and search
crawler does. This monitor reads the same structured data shops publish for
Google, politely paced, without crossing login walls or bypassing anti-bot
protections — blocked shops are reported as unreachable, not fought. And the
output is safe by construction: no shop-owner names, no contact details, ever
(see the compliance section below).
MAP enforcement itself is a legal matter that varies by jurisdiction (US MAP policies vs EU resale-price rules) — this tool gives you the evidence, not legal advice.
Compliance
This monitor collects organizational and product data only: advertised prices, availability, product names and retailer domains from public product pages — the same data the shops publish to Google. Personal data (shop-owner names, contact details, review authors) present in a page's raw structured data is structurally excluded: output items are built from a fixed whitelist of typed fields, proven by an automated test that feeds the mapper deliberately person-laden pages. No login walls are crossed, no anti-bot protections are bypassed, and unreachable shops are reported as such rather than fought.
FAQ
Which shops does it work on? Any shop that publishes structured price data — most Shopify, WooCommerce, BigCommerce, Magento and custom storefronts do (it is how they get Google rich results). Measured coverage and the honest exclusions are in the coverage section above.
Why didn't I get an alert for a violation I can see? If the violation was
already known at that price, that is the design: an alert means something
changed. Check the baseline run or the state store; resetState: true re-baselines
everything.
Does a sale price count as a violation? Yes — the monitor compares your MAP against the price the shopper actually sees (the sale price), never the crossed-out list price.
What about products with multiple variants? The cheapest advertised variant
is compared (a violating variant is a violation), flagged with
priceIsVariantLow: true and the range top in priceHigh.
Can I monitor different currencies? Yes — per product. A euro MAP is never
compared against a dollar price: currency mismatches become uncharged
currency-mismatch items instead.
How often should it run? Daily fits most MAP programs; hourly during launch weeks or known promo periods. You pay per alert, not per check, so frequency is cheap.
Amazon and Walmart? Not in v1, stated honestly: they publish no structured price data and/or actively block automation. This monitor is built for the long-tail dealer web where your authorized retailers actually live.
Troubleshooting
- "Invalid input: products: Add at least one product" — the
productsarray is empty or malformed; each entry needs at leasturlandmapPrice. - "Monitor state record 'map-products' is not readable" — the state store
holds a record written by an incompatible version. Run once with
resetState: trueto start fresh (you'll get a new baseline). status: "unreadable"for a shop you can open in a browser — the shop renders its price only in JavaScript or publishes no structured data. Not covered in v1; the item is uncharged. Thenotefield says what was missing.status: "unreachable"with HTTP 403 — the shop blocks datacenter traffic. Try a residential proxy (Input → Proxy); if it still blocks, the shop is outside honest coverage.status: "currency-mismatch"— the page advertises a different currency than the product'scurrencyinput. Fix the input currency, or set the proxy country so the shop serves your target market's prices.- Prices look right but alerts reference an old MAP — you edited
mapPriceand the change alerts only on transitions; an already-known violation at the same advertised price and MAP stays silent by design (see "How the monitor thinks").
Something else off? Open an issue on the actor's Issues tab — reports are read and answered.