MAP Price Violation Monitor & Alerts avatar

MAP Price Violation Monitor & Alerts

Pricing

from $2.00 / 1,000 baseline price snapshots

Go to Apify Store
MAP Price Violation Monitor & Alerts

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

Lowland Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 hours ago

Last modified

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)

  1. 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.
  2. 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).
  3. 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:

  • advertisedPrice is 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 in priceHigh).
  • currencyVerified: false means 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 uncharged currency-mismatch item instead.
  • priceSource tells 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 product
page: { "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 3
entries 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 alerts
automatically, 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 unreadable items — uncharged.
    • Login-walled or challenge-walled pagesunreachable, 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. Turn emitBaseline off 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 mapPrice re-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, default map-monitor-state) in your account. One monitor per store: give each brand, market or client its own store name. resetState: true wipes 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

FieldTypeDefaultNotes
productsarray3 demo products1–500 entries: url, mapPrice, optional currency (ISO, default USD), optional label.
alertOnenumviolationsviolations or violations-and-recoveries.
emitBaselinebooleantruePush first-sight snapshots; off = silent seeding.
resetStatebooleanfalseForget everything; next run is a full baseline.
maxItemsinteger500Per-run push cap. Alerts are pushed before baselines; overflow alerts on the next run.
stateStoreNamestringmap-monitor-stateKey-value store holding monitor memory; one per independent monitor.
proxyConfigurationobjectApify datacenterSet 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.js
import { 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();
# Python
from apify_client import ApifyClient
client = 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?"

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 products array is empty or malformed; each entry needs at least url and mapPrice.
  • "Monitor state record 'map-products' is not readable" — the state store holds a record written by an incompatible version. Run once with resetState: true to 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. The note field 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's currency input. 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 mapPrice and 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.