Data Tooly logo Data Tooly
Guide · E-commerce Intelligence

MAP Monitoring: Find Minimum Advertised Price Violations

How to monitor MAP: list every seller of your products from Google Shopping and retailer pages, flag advertised prices below MAP, and keep timestamped evidence.

Published · Updated · By Omar Eldeeb

MAP monitoring means listing every seller that advertises your products, comparing each advertised price with your minimum advertised price, and logging every price below it with the seller, the time and a link. Collect sellers per product from Google Shopping's seller list and from retailer pages, filter out used and refurbished offers, and run it daily so you have dated evidence, not screenshots from memory.

This guide does that with the Google Shopping "Sellers" mode and retailer URLs of E-commerce Product Scraper Pro, using real seller data for the Sony WH-1000XM5 collected on 2026-10-18 and 2026-10-20. It isn't legal advice.

What MAP is (and isn't)

A minimum advertised price (MAP) policy is a brand's rule about the lowest price its resellers may advertise for a product. This guide uses the US context. The US Federal Trade Commission's guide on manufacturer-imposed requirements describes the typical form from a dealer's point of view, where a dealer "can't participate if I advertise a price that is below the supplier's minimum advertised price", and says "the law allows a manufacturer considerable leeway in setting the terms for advertising that it helps to pay for". The same page notes the FTC challenged MAP policies of five large music distributors that it found unreasonable in reach. Policies differ, so what counts as "advertised" and what happens after a violation comes from your own policy, and its legal side is a question for your lawyer.

For monitoring, three practical points follow:

  • Advertised is not the same as sold. A retailer can show "see price in cart" and charge less at checkout. Monitoring catches what's shown.
  • Condition matters. Check whether your policy covers refurbished, renewed and used offers; either way, tag them and keep them apart from new ones.
  • Not every seller is in scope. You probably don't police the brand's own store, and some sellers may be authorised for promotions. Keep an exempt list.

Source 1: Google Shopping's seller list

Google Shopping groups offers from many stores under one product, and the actor's Sellers mode reads that list: merchant, price, old price, stock, condition, delivery, returns and the offer link, one entry per store. It's the fastest way to see sellers you didn't know about, including stores this actor can't scrape directly (Best Buy, eBay, Lowe's and Newegg pages are unsupported, but they appear in Google's seller lists, which is a different page).

The input from a run on 2026-10-18:

json
{
  "searchEngineKeyword": "Sony WH-1000XM5",
  "scrapeModeSearchEngine": "Sellers",
  "countryCode": "us",
  "maxSearchEngineProducts": 4,
  "maxSearchEngineSellersPerProduct": 5,
  "maxSearchEngineResults": 20
}

searchEngineKeyword takes up to 50 lines, one search each. Use your exact model name, or the model number, so Google groups the right product.

One output row, trimmed (the offer links are long google.com/goto redirects to each store's offer):

json
{
  "recordType": "search-result",
  "keyword": "Sony WH-1000XM5",
  "title": "Sony WH-1000XM5 Noise-Canceling Wireless Headphones",
  "priceInsight": "Typically $199–$300",
  "lowestOfferPrice": 228,
  "sellerCount": 4,
  "sellers": [
    { "merchantName": "Store A", "price": 228, "originalPrice": 398, "inStock": true, "condition": null, "bestPrice": true, "url": "https://www.google.com/goto?url=…" },
    { "merchantName": "Sony", "price": 228, "originalPrice": 400, "inStock": true, "condition": null },
    { "merchantName": "Store B", "price": 299, "inStock": null, "condition": null },
    { "merchantName": "Target", "price": 398.99, "inStock": true, "condition": null }
  ],
  "scrapedAt": "2026-10-18T09:14:51.617Z"
}

The other three products in that run included a Target "refurbished" offer at $189.99 and an Electronic Express offer tagged used: seller badges such as "Refurbished" and "Used" are split out of the merchant name into condition.

Source 2: retailer and marketplace pages

Google's list is a summary. To confirm on the retailer's own page, or to cover marketplace sellers Google doesn't show, scrape the product pages and searches directly on supported sites (Amazon, Walmart, Target, Shopify and WooCommerce stores, and the rest of the README matrix):

json
{
  "detailsUrls": [{ "url": "https://www.amazon.com/dp/B09XS7JWHH" }],
  "listingUrls": [{ "url": "https://www.walmart.com/search?q=sony+wh-1000xm5" }],
  "maxItemsPerStartUrl": 3,
  "maxProductResults": 4
}

Run on 2026-10-20, it returned the Amazon listing at $199.99 and three Walmart marketplace listings with their seller field: "Video & Audio Center" at $288, "Electronic Express" at $298 (pink), and a "Restored" unit from "Secondipity" at $147.26. Product rows have no condition field, so for direct pages filter by title words such as "Restored", "Renewed" or "Refurbished", and by the URL (Walmart's restored listing carried conditionGroupCode=2).

Flag violations and keep evidence

This reads a Sellers-mode run, skips used/refurbished offers and exempt merchants, flags prices below MAP, and appends every flag to a CSV evidence log:

python
"""Flag sellers advertising below your MAP in a Google Shopping "Sellers" run. pip install apify-client"""
import csv
import os
import sys

from apify_client import ApifyClient

MAP_USD = {"Sony WH-1000XM5": 250.00}        # made-up MAP for the example; use your policy's figures
EXEMPT = {"Sony"}                             # merchants you don't police (e.g. the brand's own store)

run = ApifyClient(os.environ["APIFY_TOKEN"]).run(os.environ["RUN_ID"])
rows = run.dataset().list_items(clean=True).items

violations = []
for row in rows:
    if row.get("recordType") != "search-result":
        continue
    map_price = MAP_USD.get(row["keyword"])
    for s in row.get("sellers") or []:
        if s.get("condition"):                 # "refurbished", "used": skip unless your policy covers them
            continue
        if s["merchantName"] in EXEMPT or s.get("currency") != "USD" or s.get("price") is None:
            continue
        if map_price and s["price"] < map_price:
            violations.append({
                "checked_at": row["scrapedAt"], "keyword": row["keyword"], "listing": row["title"],
                "merchant": s["merchantName"], "advertised": s["price"], "map": map_price,
                "gap_pct": round(100 * (s["price"] - map_price) / map_price, 1),
                "was_price": s.get("originalPrice"), "offer_link": s["url"], "google_page": row["url"],
                "run_id": os.environ["RUN_ID"],
            })

w = csv.DictWriter(sys.stdout, fieldnames=["checked_at", "merchant", "advertised", "map", "gap_pct", "listing"],
                   extrasaction="ignore")
w.writeheader()
w.writerows(violations)
if violations:                                 # append the full rows to your evidence log
    new_file = not os.path.exists("map_evidence.csv")
    with open("map_evidence.csv", "a", newline="", encoding="utf-8") as f:
        log = csv.DictWriter(f, fieldnames=list(violations[0]))
        if new_file:
            log.writeheader()
        log.writerows(violations)

Output for the 2026-10-18 run (RUN_ID=FiUR4s8DgTfDEE02e):

text
checked_at,merchant,advertised,map,gap_pct,listing
2026-10-18T09:14:51.617Z,Store A,228,250.0,-8.8,Sony WH-1000XM5 Noise-Canceling Wireless Headphones

The $250 MAP is made up for this example, and the merchant names are replaced with "Store A" and "Store B". Sony's actual policy, if it has one for this model, isn't public, and nothing here says any retailer broke one. With that test number, the script flagged one offer and correctly skipped: Sony's own store (exempt), Target's and Electronic Express's refurbished/used offers (condition), and the offers at or above $250.

map_evidence.csv keeps the full record for each flag: checked_at, keyword, listing, merchant, advertised, map, gap_pct, was_price, offer_link, google_page and run_id. That's the core of what a MAP enforcement email needs. For a stronger record:

  • Confirm on the seller's page for supported retailers (Source 2) before you act, and store that row too. Google's price can lag the store's.
  • Keep the raw rows. Export each run's dataset (JSON or CSV) to your own storage instead of relying on the run being kept on Apify.
  • Add a screenshot if your policy process needs one. The actor doesn't take screenshots, so use a browser tool on the flagged links.
  • Log repeats. The same seller below MAP on three consecutive daily checks is a different conversation from a one-hour glitch.

Schedule and cost

Save the Sellers input as a task and add an Apify schedule, for example 0 9 * * * (09:00 daily in the schedule's timezone).

The actor is pay per event. In the README's pricing table, a Google Shopping request costs $0.0045 on FREE and $0.0035 on GOLD (billed per successful request; failed ones aren't billed), a seller row $0.0024 and $0.0008, and the run start $0.005. The 2026-10-18 run billed 4 search-engine requests and 9 seller rows:

  • FREE: 4 × $0.0045 + 9 × $0.0024 + $0.005 = $0.0446
  • GOLD: 4 × $0.0035 + 9 × $0.0008 + $0.005 = $0.0262

In Sellers mode each product costs one extra Google request, so cost grows with maxSearchEngineProducts × keywords, plus one seller row per store listed. Use maxSearchEngineSellersPerProduct to cap the long tail. Apify's free plan gives $5 of usage a month with no credit card; the actor has no in-actor free trial.

During Black Friday and the holiday season

MAP pressure peaks when everyone discounts. Some brands also suspend or relax MAP for a set window, a "MAP holiday". For example, board-game publisher Iello suspended its MAP for Black Friday weekend in 2017, letting retailers "advertise games at prices below 20% off MSRP" (ICv2). If your policy has such a window, put its dates and temporary floor into the check instead of flagging authorised promotions. For the busiest window, add a second schedule to the same task such as 0 */3 20-30 11 *, every three hours from 20 to 30 November 2026 (88 runs in America/New_York; at the run size above, 88 × $0.0446 = $3.92 on FREE or 88 × $0.0262 = $2.31 on GOLD per keyword). Black Friday is Friday 27 November and Cyber Monday 30 November 2026. The holiday sales calendar has every event's window, and comparing Black Friday prices across stores uses the same Google Shopping data from the shopper's side.

Limits

  • Google Shopping's "Reviews" mode isn't available, and an answer for the wrong market gets one retry and is then reported as blocked (not billed). Google may truncate long product titles.
  • Direct checks only work on supported sites; Best Buy, eBay, Home Depot, Lowe's, Costco and Newegg pages are not supported.
  • Google's grouping isn't perfect: listings for different colours, bundles or the previous model can share a search. Check title before you send anything.

Related: competitor price monitoring, price drop alerts in Slack, and the e-commerce price tracker query builder.

Disclosure: I built the actor and this site. The flagging and evidence logic works with seller data from any source.

Sources: FTC: Manufacturer-imposed requirements, ICv2: Iello suspends MAP policy (2017), E-commerce Product Scraper Pro README (Sellers mode, pricing, supported sites, limitations), Apify docs: Schedules, and actor runs on 2026-10-18 and 2026-10-20.

Quick answers

What is a MAP violation?

A reseller advertising a product below the minimum advertised price that the brand's MAP policy sets. What counts as 'advertised' (a product page, a feed to Google Shopping, an email, a price shown only in the cart) and what happens next are defined by each brand's own policy, not by a single standard.

Is MAP the same as the price a retailer can sell at?

No. MAP limits the price a reseller advertises. Policies typically leave the actual selling price to the retailer, which is why some retailers show 'see price in cart'. Check the wording of the specific policy, and ask a lawyer about how it applies to you.

How do brands find MAP violators?

They list the sellers of each product (Google Shopping's seller lists, marketplace offer pages, retailer sites), compare each advertised price with the MAP, and log every violation with a timestamp, the seller, the price and a link, typically on a daily schedule. Paid MAP-monitoring services do this as a managed product; you can also script it.

Do refurbished or used listings count toward MAP?

It depends on the wording of the policy. Either way, separate condition in your data: the Sellers mode output in this guide tags refurbished and used offers in a condition field so they aren't mixed in.

Open the free E-commerce price tracker tool →

Written by Omar Eldeeb, who also builds the tools and Apify actors mentioned here. Links to apify.com carry an affiliate parameter — see the disclosure.