๐ท๏ธ MAP Violation Detector - Reseller Price Enforcement
Pricing
Pay per event
๐ท๏ธ MAP Violation Detector - Reseller Price Enforcement
๐ท๏ธ Match retailer listings to your brand catalogue and flag Minimum Advertised Price (MAP) violations, with enforcement-ready evidence. โ Brand/model/size/pack hard disqualifiers block wrong-SKU matches. โ ๏ธ Low-confidence matches are reported unmatched, never accused.
Pricing
Pay per event
Rating
0.0
(0)
Developer
mohamed alaya
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
MAP Violation Detector
Brands set a Minimum Advertised Price (MAP); resellers sometimes advertise below it anyway. This actor matches observed retailer listings against your brand catalogue, compares the observed price to MAP with a configurable tolerance, and emits an evidence row per violation โ retailer, listing URL, observed price, MAP, delta, match confidence, matched-on fields, timestamp โ the kind of thing that goes straight into an enforcement letter. Brands pay real money for exactly this.
Why the matching step is the whole ballgame
Getting the match wrong means accusing a reseller of violating MAP on the wrong product. That
is not a cosmetic bug, it is a legal and reputational risk. So this actor reuses the
product-matcher actor's matching approach (adapted, not imported โ this actor has no
cross-actor dependency): brand, model/part number, and GTIN identity, plus size and pack-count
canonicalisation ("500ml" vs "0.5L" vs "16.9 fl oz" all compare correctly). Crucially, a
different size or pack count is a HARD DISQUALIFIER โ it blocks a match even when the titles
are near-identical, before any weighted scoring runs. A 500ml bottle and a 1L bottle of the exact
same brand are NOT the same SKU, and this actor will never say they are.
Any listing that cannot be matched with at least matchThreshold confidence is reported as
unmatched, never scored as a violation. Match confidence is included on every row so a human
can audit the call.
What it does
- Input โ a brand catalogue (
catalogue: product identity + MAP price per SKU) and observed listings (listings: retailer, title, price, url), inline and/or via Apify dataset IDs. Listing prices can also be scraped directly fromlistingUrlsusing the sharedmonitor-coreengine's selector-free price detection (JSON-LD โ microdata/OG โ common price containers โ text-scan fallback). - Match each listing to its best-scoring catalogue candidate using brand/model/GTIN identity and canonicalised size/pack-count, with blocking so large catalogues stay fast.
- Compare price vs MAP with a configurable absolute and/or percent tolerance
(
toleranceAbsolute,tolerancePercent,toleranceMode), currency-aware (requireSameCurrencyโ a currency mismatch is reported unmatched rather than silently mis-compared, since there is no live FX conversion here). - Classify every listing as
compliant,violation,below-threshold-but-within-tolerance, orunmatched. - Severity on every violation:
deltaAbsolute,deltaPercent, and aminor/moderate/severebucket (10% / 25% breakpoints). - Per-retailer summary (
RETAILER_SUMMARYin the key-value store, and inline inSUMMARY): violation count, worst offender (URL + delta), average discount below MAP. - Optional repeat-offender tracking (
trackRepeatOffenders) persists cumulative per-retailer violation counts in a named key-value store across runs.
Input
{"catalogue": [{ "sku": "ACME-500", "title": "Acme Hydro Flask 500ml", "brand": "Acme", "size": "500ml", "map": 24.99, "currency": "USD" }],"listings": [{ "retailer": "discount-outlet.example", "title": "Acme Hydro Flask 500ml Bottle", "price": 17.99, "url": "https://discount-outlet.example/p/123", "currency": "USD" }],"matchThreshold": 80,"tolerancePercent": 2}
Output
One row per checked listing (unless filtered by includeCompliant/includeUnmatched): its
classification, severity (violations only), observedPrice, mapPrice, deltaAbsolute,
deltaPercent, matchConfidence, matchedOn (which fields agreed), and full per-field
evidence. A SUMMARY key-value record with run stats and the per-retailer breakdown.
Honest limitations โ read before you act on this
- Match confidence matters legally. This is text/structured-attribute matching, not image or
barcode-verified identity. A confident-looking title match is still a probabilistic call.
Anything below
matchThresholdis reportedunmatched, on purpose โ never guess your way into an accusation. - No live currency conversion. Cross-currency listings are reported
unmatched(currency-mismatch) rather than compared using a stale or assumed rate. - Prices must share one unit convention.
toleranceAbsoluteis a whole number in the SAME currency-unit convention (dollars, or cents โ pick one) as your catalogue/listing prices; the catalogue/listing price fields themselves are plain numbers, not schema-restricted. - GTIN/EAN/UPC/ASIN are normalised but never checksum-validated.
- Bare "oz" is treated as fluid ounce, matching common retail convention โ pass an explicit
sizefield to avoid ambiguity for weight-in-ounces products. - Scraped listing prices depend on page structure.
detectPrice's JSON-LD/microdata/selector fallback chain is reliable on most modern e-commerce pages but can miss on unusual layouts โ those rows report an explicit scrape error rather than a fabricated price. - Capped by the same blocking safety valve as
product-matcher: oversized blocks are skipped rather than compared exhaustively.