MAP Violation Monitor — Price Floor Compliance Scraper avatar

MAP Violation Monitor — Price Floor Compliance Scraper

Pricing

from $2.00 / 1,000 result scrapeds

Go to Apify Store
MAP Violation Monitor — Price Floor Compliance Scraper

MAP Violation Monitor — Price Floor Compliance Scraper

Monitor MAP (Minimum Advertised Price) compliance for your products across European price-comparison sites. Runs Geizhals, Ceneo and Prisjakt by default (Idealo, Heureka, Skroutz optional), flags offers advertised below your floor price, and tracks new vs. recurring violations run over run.

Pricing

from $2.00 / 1,000 result scrapeds

Rating

0.0

(0)

Developer

Studio Amba

Studio Amba

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

Find Minimum Advertised Price (MAP) violations for your products across European price-comparison sites in one run. Give the actor a product list with a floor price per product, and it returns only the offers advertised below that floor — which engine found it, the market, how far below floor, and whether it is new or a violation that is still ongoing since your last run.

Brands and distributors pay agencies and manual "pige" teams to watch this. MAP Violation Monitor runs the check on a schedule instead.

What it does

  • Runs our own price-comparison scrapers in parallel. Geizhals (AT/DE) by default — the only one of the six confirmed working end-to-end on a plain residential proxy. Ceneo (PL), Prisjakt (SE), Idealo (DE), Heureka (CZ) and Skroutz (GR) are all available opt-in; see the reliability table below before adding them.
  • Matches each engine's result back to your product. EAN/barcode match when available (Ceneo exposes a barcode field), fuzzy product-name match otherwise.
  • Converts every price to EUR using ECB daily reference rates, so a floor price in EUR can be checked against an offer in PLN or SEK without you doing the conversion.
  • Returns only violations. No noise — one row per matched offer that sits below your floor price, with the exact percentage below floor.
  • New-vs-recurring delta. Schedule the actor daily and each violation is flagged isNew the first time it appears, then false on every following run while it stays active — so you can see which resellers keep re-offending.

How to scrape MAP violation data across price-comparison sites

  1. Enter your products — each one needs a name (used as the search query on every engine) and a floorPrice. Add ean if you have it and currency if it isn't EUR.
  2. Pick your engines. Geizhals is the cloud-verified reliable default. Add Ceneo, Prisjakt, Idealo, Heureka or Skroutz for broader coverage — see the reliability table below before you do; several currently return nothing.
  3. Run it. Each row is one violation: product, engine, market, advertised price, floor price, and how far below floor it sits.
  4. Schedule it daily or weekly. The new-vs-recurring delta tells you which violations are fresh and which resellers haven't corrected their pricing.

Honest limitation: no per-seller data

None of the six bundled engines expose a per-seller/per-merchant breakdown. Each one returns a single aggregate "lowest price found" per product page, plus (on most of them) a count of how many resellers that price was drawn from. There is no seller name, no per-offer list.

So sellerOrSite in the output is honestly the comparison engine itself (e.g. "Geizhals"), not a named reseller. offerCount tells you how many resellers that aggregate price was pulled from, where the engine exposes it. If you need reseller-level attribution, you would need a scraper built against the individual retailer sites, not a price-comparison aggregator — that's a different (and much larger) build than this one.

This is a deliberate, disclosed limitation, not a bug: price-comparison engines are the fastest way to catch that a violation exists across a market; they were never going to tell you which of the 12 retailers listed on them did it.

Engines and reliability (method disclosure)

Cloud-verified Jul 2026 — every engine below was actually run, not just read from its source code.

EngineMarketCurrencyMethodReliabilityNotes
GeizhalsAT/DEEURResidential proxy + parsingHighCloud-verified working: real offers, real matches, real violations. Default.
CeneoPLPLNResidential proxy + parsingBroken (child bug)Also exposes EAN — the only engine that does — but its own detail-page parser is currently failing on live markup ("No product name on ..." warnings): it finds the listing, extracts nothing. Opt-in until healed.
PrisjaktSESEKResidential proxy + parsingBlockedServes a Cloudflare "Just a moment..." JS challenge on its search endpoint that a non-browser (Cheerio) crawler can't solve, even through a residential Swedish IP. Opt-in.
IdealoDEEURBright Data Scraping BrowserLowHard-blocked on standard Web Unlocker as of Jul 2026; needs a live Scraping Browser CDP endpoint configured on the child actor. Opt-in.
HeurekaCZCZKBright Data Web UnlockerMediumDataDome-class protection; needs a Bright Data API key configured on the child actor. Opt-in.
SkroutzGREURBright Data Web UnlockerMediumCloudflare "Just a moment" challenge even on Greek residential IPs; needs Bright Data. Opt-in.

All six children get a RESIDENTIAL proxy scoped to their market by default — none of them work reliably on a plain/datacenter proxy, including the three with no explicit anti-bot code path. The actor degrades gracefully: if an engine fails or returns nothing for a product, the run continues with the others and the run summary records which engines succeeded and which failed.

Documented limits

  • No per-seller attribution. See above — output is engine-level, not merchant-level.
  • Query is by product name, not EAN. None of the six engines support searching by barcode directly, so every product is searched by its name. EAN is only used to improve matching after results come back, and only on the one engine that exposes a barcode field (Ceneo).
  • Fuzzy name matching can miss or mismatch. A very short or generic product name (e.g. just a brand) risks a false match on a different SKU. Use a specific model name and, where possible, tune matchThreshold up for stricter matching or down for wider recall.
  • FX rates are daily ECB reference rates, not live market rates — fine for catching a MAP violation, not for accounting.
  • 5 of the 6 engines are currently opt-in, not default, for real cloud-verified reasons — a broken child parser (Ceneo), a Cloudflare JS challenge (Prisjakt), or a Bright Data dependency (Idealo/Heureka/Skroutz). Only Geizhals is the default. Adding the others is safe (the run degrades gracefully) but don't expect data from them until they're healed/upgraded.

Output fields

Each row is one violation:

  • productName, ean, floorPrice, floorCurrency — the product you're monitoring
  • engine, market, sellerOrSite, offerCount — where the violation was found
  • advertisedPrice, currency, advertisedPriceEur, floorPriceEur, pctBelowFloor — the pricing detail
  • matchType, matchScore — how confident the match to your product is (ean = high confidence, name = fuzzy)
  • url — link to the offending listing
  • isNew, firstSeenAt, stillActive — the run-over-run delta
  • scrapedAt — when this run happened

Example output

{
"productName": "Sony WH-1000XM5",
"ean": null,
"floorPrice": 320,
"floorCurrency": "EUR",
"market": "AT/DE",
"engine": "geizhals",
"sellerOrSite": "Geizhals",
"offerCount": 24,
"advertisedPrice": 279,
"currency": "EUR",
"advertisedPriceEur": 279,
"floorPriceEur": 320,
"pctBelowFloor": 12.8,
"matchType": "name",
"matchScore": 0.75,
"url": "https://geizhals.at/...",
"isNew": true,
"firstSeenAt": "2026-07-14T10:00:00.000Z",
"stillActive": true,
"scrapedAt": "2026-07-14T10:00:00.000Z"
}

How matching works

  1. EAN match, when available. If your product has an ean and an engine result exposes one (currently Ceneo only), an exact barcode match is used and scored at full confidence.
  2. Fuzzy name match, otherwise. Product names are normalized (lowercased, punctuation stripped) and compared as word sets — the overlap ratio is the match score. The best-scoring result above matchThreshold (default 0.4) is used.
  3. No match above threshold means no row. A product not found on an engine, or found but not confidently matched, produces neither a violation row nor an error — it's simply absent from the output for that engine.

A run with zero violations because nothing was below floor is a success, not a failure. The actor only fails when every engine call returned zero usable offers — a real infrastructure problem, not an empty result.

Who uses this

  • Brands and distributors enforcing MAP agreements with resellers across EU markets.
  • Channel managers who need to know which reseller is undercutting the floor before a manual audit.
  • Pricing and revenue teams tracking whether a competitor's grey-market inventory is leaking below agreed pricing.

Pricing

Pay per result. You pay for the run start and for each violation row returned — that row is the entire deliverable, so there's no separate "raw data" tier. See the pricing tab for current rates.

FAQ

Can I monitor a product across multiple currencies? Yes. Set floorPrice and currency once per product; the actor converts every engine's local price to EUR using the same day's ECB rate before comparing, so a EUR floor is checked correctly against PLN and SEK offers.

Does it tell me which specific retailer is violating? No — see the "Honest limitation" section above. It tells you which comparison engine and market the violation was found on, and how many resellers that aggregate price was drawn from where the engine exposes a count.

How do I get a feed of only new violations? Schedule the actor and filter your downstream consumer on isNew: true. The per-account key-value store remembers what was seen on the previous run.

Do I need a proxy? No — the actor defaults every engine to a RESIDENTIAL proxy scoped to its market automatically. You only need to supply your own proxyConfiguration if you want to override that (e.g. use a different proxy group). Note that a proxy alone won't fix Ceneo's current parser bug or Prisjakt's Cloudflare challenge, and Idealo/Heureka/Skroutz additionally need their own Bright Data configuration.

Is this legal? The actor collects publicly advertised prices from public comparison sites. You are responsible for how you use the output, including any contractual enforcement action against resellers.