# Amazon MAP & Buy Box Evidence Monitor (`bread-kim/amazon-map-buy-box-evidence-monitor`) Actor

Track Amazon MAP violations and Buy Box changes by ASIN. Get evidence-ready price, seller, and offer results for monitoring and compliance.

- **URL**: https://apify.com/bread-kim/amazon-map-buy-box-evidence-monitor.md
- **Developed by:** [bread kim](https://apify.com/bread-kim) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Amazon MAP & Buy Box Evidence Monitor

A private Apify Actor for brand-protection teams. It checks up to 1,000 user-declared public HTTPS product pages, extracts the visible offer price and Buy Box seller, applies safely quantifiable coupons, compares the result with a supplied MAP threshold and prior seller, and produces a timestamped evidence pack.

This is an evidence triage tool—not a legal decision engine and not a bypass for site access controls. Result `status` is separate from the legacy MAP/change `finding`: EVIDENCE\_FOUND (supported price and/or featured seller), INSUFFICIENT\_DATA (verified normal product page with no offer signals), ACCESS\_LIMITED (challenge/consent), PARSE\_FAILED (unconfirmed/unsupported page or malformed supported signal), FETCH\_FAILED (HTTP/network failure). Only the first two are successful investigations. Others never pass the Canary gate.

Normal-page proof requires the existing product title and a page ASIN matching the requested Amazon product path. Missing price/seller is represented by null, not a default. Without price, mapEvaluation is NOT\_EVALUATED; false candidate flags are not a compliance conclusion. Generic JSON-LD merchants and other-offer sellers are not treated as the featured Buy Box seller. Observed signals retain bounded raw text, source, normalized value, URL and timestamp. Currency is the configured comparison currency, not independently inferred.

### What the run produces

- `OUTPUT` in the default key-value store: the authoritative run record defined by `tests/output-record.schema.json`.
- One dataset row per target for filtering and export.
- A PNG evidence card per successfully fetched target. It is a screenshot of the normalized observation, not a claim to be a pixel-perfect screenshot of the remote page.
- Optional fetched HTML, disabled by default.
- SHA-256 of fetched response bytes for later integrity checks.

### Input

See `examples/input.json`. Each target requires an HTTPS URL and may include `asin`, `mapPrice`, `previousBuyBoxSeller`, and `label`. Currency is run-wide. The Actor does not convert currencies.

```json
{
  "targets": [{
    "url": "https://www.amazon.com/dp/B08N5WRWNW",
    "mapPrice": 30,
    "previousBuyBoxSeller": "Authorized Retailer"
  }],
  "currency": "USD"
}
```

Coupon-adjusted price is calculated only for unambiguous fixed-value or percentage coupons visible in supported markup. Eligibility, clipped-coupon state, tax, shipping, subscription discounts, and checkout-only promotions are not inferred.

### Local verification

Requires Node.js 20+.

```bash
npm ci
npm run typecheck
npm test
npm run build
```

To run locally with Apify storage emulation:

```bash
APIFY_INPUT_KEY=INPUT npm start
```

Place input through the normal Apify CLI/local-storage workflow. Local tests use synthetic credentials only; no paid proxy requests occur locally.

### Limited Residential fallback

Direct HTTPS is always attempted first, at most once per target. Only a classified access-limited page or HTTP 403/429 enables official Apify RESIDENTIAL/US fallback, only inside the Apify runtime and only for amazon.com/www.amazon.com. Normal INSUFFICIENT\_DATA never triggers proxy usage. At most two Residential requests (including redirect hops) use distinct SDK sessions; the first normal page ends fallback. There are no implicit retries, browser assets, CAPTCHA solving, or third-party proxies.

Each Residential CONNECT uses a validated public DNS pin while preserving the Amazon TLS hostname and Host header. Every redirect is revalidated. Encoded and decoded body limits remain enforced. SDK credentials remain in memory, verbose transport debugging is rejected, and authenticated proxy URLs are never stored or logged. Results expose only bounded attempt counts, response bytes, route and direct classification. Group access failure returns ACCESS\_LIMITED with RESIDENTIAL\_PROXY\_UNAVAILABLE; other transport errors do not pretend to be missing offer evidence.

Residential traffic is billed by Apify. See PRICING\_PROPOSAL.md; pricing is a proposal only. The private canary requires an explicit Actor/bundle-specific proxy cost approval; default platform policy still denies proxy spending.

### Security boundaries

Only user-declared HTTPS port 443 targets are fetched. Every initial URL and redirect is validated; DNS answers containing private/reserved addresses are rejected; the approved public address is pinned into the TLS request to prevent DNS rebinding; certificate validation retains the original hostname. Redirects, compressed and decompressed bytes, timeout, and concurrency are bounded. See `SECURITY_REVIEW.md`.

### Operational expectations

`tests/output-record.schema.json` is the structural OUTPUT contract and represents all five investigation statuses, including failed fetches with unobserved fields. `tests/canary-output.schema.json` is a separate, stricter one-target functional acceptance contract used by the production private lifecycle. Its legacy `outputValid` receipt means functional acceptance, not general OUTPUT structural validity. A structurally valid ACCESS\_LIMITED, PARSE\_FAILED, or FETCH\_FAILED record never qualifies for READY\_TO\_PUBLISH.

Static HTML extraction is deliberate: it keeps network behavior auditable and blocks browser subresource sprawl. A verified normal product page with no offer signals returns INSUFFICIENT\_DATA. A present but uninterpretable price signal returns PARSE\_FAILED; an unverified layout cannot be labeled insufficient. A human should open the evidence card and source URL before any enforcement action.

### Status

Private candidate: READY\_TO\_PUBLISH requires the current exact build and functional Canary evidence; source inclusion or process exit 0 alone is insufficient. Publishing itself is intentionally out of scope.

# Actor input Schema

## `demoMode` (type: `boolean`):

Runs a deterministic built-in example without fetching Amazon. Intended for testing and evaluation.

## `targets` (type: `array`):

1–1,000 public HTTPS product URLs. MAP thresholds and previous sellers are user-supplied comparison baselines.

## `currency` (type: `string`):

Three-letter currency code applied to thresholds and observed prices.

## `concurrency` (type: `integer`):

Maximum product pages inspected at the same time.

## `requestTimeoutSecs` (type: `integer`):

Maximum time allowed for each page request.

## `maxResponseBytes` (type: `integer`):

Maximum compressed and decoded bytes accepted from each page.

## `maxRedirects` (type: `integer`):

Maximum validated HTTPS redirects followed for each target.

## `storeHtml` (type: `boolean`):

Off by default because HTML may contain page-specific identifiers.

## `userAgent` (type: `string`):

Bounded User-Agent header sent to each target.

## Actor input object example

```json
{
  "demoMode": true,
  "targets": [
    {
      "url": "https://www.amazon.com/dp/B000000000",
      "asin": "B000000000",
      "mapPrice": 100,
      "previousBuyBoxSeller": "Previous Seller",
      "label": "Apify QA Demo"
    }
  ],
  "currency": "USD",
  "concurrency": 1,
  "requestTimeoutSecs": 20,
  "maxResponseBytes": 2000000,
  "maxRedirects": 3,
  "storeHtml": false,
  "userAgent": "Mozilla/5.0 (compatible; MAP-Evidence-Monitor/1.0; +https://apify.com)"
}
```

# Actor output Schema

## `output` (type: `string`):

Validated run summary and all observations stored under the OUTPUT key.

## `dataset` (type: `string`):

One bounded observation record per requested product target.

## `store` (type: `string`):

PNG evidence cards and optional source HTML.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "demoMode": true,
    "targets": [
        {
            "url": "https://www.amazon.com/dp/B000000000",
            "asin": "B000000000",
            "mapPrice": 100,
            "previousBuyBoxSeller": "Previous Seller",
            "label": "Apify QA Demo"
        }
    ],
    "currency": "USD",
    "concurrency": 1,
    "requestTimeoutSecs": 20,
    "maxResponseBytes": 2000000,
    "maxRedirects": 3,
    "storeHtml": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("bread-kim/amazon-map-buy-box-evidence-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "demoMode": True,
    "targets": [{
            "url": "https://www.amazon.com/dp/B000000000",
            "asin": "B000000000",
            "mapPrice": 100,
            "previousBuyBoxSeller": "Previous Seller",
            "label": "Apify QA Demo",
        }],
    "currency": "USD",
    "concurrency": 1,
    "requestTimeoutSecs": 20,
    "maxResponseBytes": 2000000,
    "maxRedirects": 3,
    "storeHtml": False,
}

# Run the Actor and wait for it to finish
run = client.actor("bread-kim/amazon-map-buy-box-evidence-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "demoMode": true,
  "targets": [
    {
      "url": "https://www.amazon.com/dp/B000000000",
      "asin": "B000000000",
      "mapPrice": 100,
      "previousBuyBoxSeller": "Previous Seller",
      "label": "Apify QA Demo"
    }
  ],
  "currency": "USD",
  "concurrency": 1,
  "requestTimeoutSecs": 20,
  "maxResponseBytes": 2000000,
  "maxRedirects": 3,
  "storeHtml": false
}' |
apify call bread-kim/amazon-map-buy-box-evidence-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bread-kim/amazon-map-buy-box-evidence-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0ZF4WjPzr46WV1Vjw/builds/l4UeUOn4QyQCVZYmX/openapi.json
