# PricedProof - Flag Below-MAP Advertised Prices With Proof (`steadyworks/priced-proof`) Actor

Flags each advertised price below your MAP with URL+timestamp evidence rows for unilateral-policy review. $0.10/violation, $0.001/snapshot, $0.00005/start; quiet 100 URLs/day ≈ $3/mo; 5-run/7-day trial; verify-before-enforce, review-only.

- **URL**: https://apify.com/steadyworks/priced-proof.md
- **Developed by:** [steadyworks](https://apify.com/steadyworks) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $80.00 / 1,000 violations

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## PricedProof - Flag Below-MAP Advertised Prices With Proof

You set a minimum advertised price under your unilateral policy. A retailer advertises below it and you hear about it from a customer. Every day a below-minimum advertised price stays live, the discount comes out of your margin — this flags it with evidence you can review. This run checks the advertised price on each URL you list and returns one row per below-minimum advertised price with evidenceUrl + checkedAt + notice you forward only after you verify-before-enforce. Snapshots $0.001 + $0.10/violation + start $0.00005; quiet 100 URLs/day ≈ $3.00/mo — about $0.10 a day, hard-capped by the spend limit you set. Brand-protection suites start around $189/mo; this does one job for that coffee money. Each row carries evidence URL, timestamp, and a verify-before-enforce notice. Use ONLY for advertised price checks on structured-data shops (Shopify, WooCommerce, BigCommerce, custom) under your unilateral policy, up to 200 pages/run. Proceed ONLY if targets are public HTTPS retailer pages you may review; otherwise STOP. If we cannot read a page cleanly, we say so — you will never get a guessed price. Marketplace and big-box pages behind bot walls (Amazon, Walmart, Target, BestBuy, eBay, Etsy), JavaScript-rendered prices, and login-walled pages are not covered — they yield requiresAuth or fetch-failed, never a guessed price. Review-only — every row carries evidenceUrl + checkedAt + verify-before-enforce notice. No screenshots in 0.1.0 — evidence is the row fields instead.

### Demo

Run the 3-product demo first — 3 products in, exactly 1 violation out. Runs in seconds on the fixture portfolio; $0 on paid-plan trial (5 runs/7 days), otherwise bills per event from run 1 (under a cent for the demo: ≤3 snapshots + start; free-plan runs bill prepaid credit from run 1).

See proof before you commit your list.

Minimal input (copy-pasteable):

```json
{
  "targets": [
    { "url": "https://www.example-retailer.com/product-a", "mapPrice": 99 },
    { "url": "https://www.example-retailer.com/product-b", "mapPrice": 149 }
  ],
  "storeName": "acme-1"
}
```

Run 1 shows up to 4 current below-MAP rows + lockedCount — $0 on trial, billed with snapshots + start on paid (all under your cap). Runs 2+ show only changes; trial runs feed the baseline, so your first paid run bills only what's new since the trial.

### Spreadsheet → JSON in 60 seconds

Your existing sheet is enough to start.

Paste this prompt plus your rows into any AI chatbot (ChatGPT, Claude, Gemini),
then paste its answer straight into this Actor's input:

> Turn these rows into PricedProof input JSON. Each row is a product URL plus its
> minimum advertised price under my unilateral policy. Output ONLY this shape, no
> other text: {"targets": \[{"url": "...", "mapPrice": 0}], "storeName": "..."}.
> Rules: every url starts with https://; every mapPrice is a plain number, no
> currency symbols; storeName is lowercase letters, numbers and dashes only,
> max 63 chars, built from my brand: ACME; add "sku" per target: 1-3 distinctive words from the product name, lowercase letters, numbers and dashes only, no spaces, reuse identically every run (case-sensitive: Boots and boots count as different products) — without it, below-MAP prices cannot be confirmed and count as unmatched; Rows:
> `<paste your URL + price rows here>`

Worked example — these 3 sheet rows:

```text
https://shop.example.com/boots, 120
https://shop.example.com/jacket, 89.99
https://other.example.net/cap, 25
```

become:

```json
{
  "targets": [
    { "url": "https://shop.example.com/boots", "mapPrice": 120, "sku": "boots" },
    { "url": "https://shop.example.com/jacket", "mapPrice": 89.99, "sku": "jacket" },
    { "url": "https://other.example.net/cap", "mapPrice": 25, "sku": "cap" }
  ],
  "storeName": "acme-1"
}
```

URLs only, no prices yet? Paste the URLs alone — the chatbot fills mapPrice with 0
and you MUST replace every 0 with the real minimum advertised price before running
(0 is rejected at input). Every result row carries evidence URL + timestamp +
verify-before-enforce notice; this input only sets the advertised price baseline.

### Input

- `targets`: required. 1–200 items; fewer than 1 or more than 200 rejected. Each advertised price is compared against its minimum advertised price under your unilateral policy. Per item: `url` required — pattern `^https://`, 1–2048 chars, public host only (`http://`, IP literals, localhost/private hosts, userinfo rejected at input); `mapPrice` required — `0.01`–`1000000`, out-of-range rejected; `sku` optional, defaults to `url` (truncated to 256) — explicit values 1–256 chars matching `^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$`, mismatch rejected; `currency` optional, default `USD` — pattern `^[A-Z]{3}$`, mismatch rejected.
- `authorizedSellers`: optional, default `[]`. At most 50 hostnames, each at most 253 characters and a plain non-empty hostname (spaces and `/ : ? # @` rejected). Case-insensitive exact match — list subdomains explicitly (e.g. `shop.example.com`); trailing dots ignored. Matching hostnames are excluded from advertised price violation and recovery rows; snapshots still bill; verify-before-enforce applies.
- `alertMode`: optional, default `violations-only`. Enum `violations-only` | `violations-and-recoveries`; anything else rejected. `violations-and-recoveries` additionally emits recoveries for advertised prices returned to at-or-above minimum advertised price.
- `maxSpendUsd`: optional, default `2`. Spend protection, range `0.05`–`50` (outside the range the input checker stops you before anything bills); a run that would bill past the cap stops instead — snapshots billed, counts on the report, no surprise charges; raise the cap and rerun to collect the per-product evidence rows.
- `storeName`: required. Pattern `^[a-z0-9][a-z0-9-]{1,62}$`; mismatch rejected. Same `storeName` plus same targets reuses the same advertised price baseline; a different name forks a separate baseline.
- Dedup: duplicate `url`s (after canonicalization) collapse to one check; duplicate `sku` fails the run.

### Outputs

- Datasets (4): `default` (violations — advertised prices below minimum advertised price), `recoveries`, `retailer_summary` (one row per retailer hostname: `retailer`, `complianceRate`, `violationCount`, `riskLevel`), `category_summary` (single-row rollup: `violationCount`, `mostViolatedSku`, null when zero violations).
- Key-value store: `MAP_COMPLIANCE_REPORT` holds `storeName`, `checkedAt`, `notice`, `violations`, `retailerSummary`, `categorySummary`, `recoveryCount`, `spendUsd`, `chargeId`, `blockCounts`, `allFail`, `baselineReset`, `candidateCapHit`, `lockedCount`, `llmCalls`.
- Per-row guarantee: every violation and recovery row carries `evidenceUrl` + `checkedAt` + `notice` (`verify-before-enforce: advertised price only under a unilateral minimum advertised price policy; independently verify before any action.`).
- Run 1 shows up to 4 current below-MAP rows + lockedCount — $0 on trial, billed on paid. Later runs emit transitions only (`new` / `deeper-cut`, plus `recovery` only when `alertMode` is `violations-and-recoveries`). Cap-tripped runs deliver counts only with no evidence rows. `unmatched` counts advertised prices below the minimum advertised price whose identity could not be confirmed — add SKUs to convert these into evidence rows.

Canonical identifiers: events `pricedproof-snapshot`, `pricedproof-violation`, `pricedproof-recovery`, `pricedproof-start`; datasets `default`, `recoveries`, `retailer_summary`, `category_summary`; key-value store `MAP_COMPLIANCE_REPORT`; every row carries `evidenceUrl` + `checkedAt` + `notice`.

### Price monitoring scope and limits

Structured-data shops only (Shopify, WooCommerce, BigCommerce, custom). Marketplace and big-box pages behind bot walls, JavaScript-rendered prices, and login-walled pages are not covered — they yield `requiresAuth` or `fetch-failed`, never a guessed price. No screenshots in 0.1.x — evidence is the row fields instead.

### Minimum advertised price evidence fields

Every violation and recovery row carries `evidenceUrl` (the exact page checked) + `checkedAt` (ISO timestamp of the check) + `notice` (`verify-before-enforce: advertised price only under a unilateral minimum advertised price policy; independently verify before any action.`). Review-only: forward rows only after you verify.

### Billing

| Event | Apify event name | Bronze price |
|---|---|---|
| snapshot | `pricedproof-snapshot` | $0.001 |
| violation | `pricedproof-violation` | $0.10 |
| recovery | `pricedproof-recovery` | $0.02 |
| start | `pricedproof-start` | $0.00005 |

Formula: `cost = S×0.001 + V×0.10 + R×0.02 + 0.00005 (if started)`.

- `snapshot` bills once per URL iff an advertised price was extracted or `requiresAuth` is true. Failed fetches bill $0.
- `violation` and `recovery` bill per emitted row. `authorizedSellers` exclusions emit no row and bill $0.
- `start` bills once per run per attempt, including all-fail runs. It is non-refundable.
- Quiet portfolio: 100 URLs/day × 30 days = 3000 `snapshot` events ≈ $3.00/mo. Each run adds one `start`.
- Spike month: 20 `violation` events add 20 × $0.10 = +$2.00. Snapshots and `start` still apply.
- Capped run: 200 `snapshot` + 30 `violation` + one `start` = 200×0.001 + 30×0.10 + 0.00005 = $3.20005; set `maxSpendUsd` 3.21 or higher to keep evidence rows.

### Limits

`Trial:` 5 runs within 7 days, $0 on Apify paid plans — run 1 shows up to 4 current rows + lockedCount; runs 2+ absorb into the baseline. Each trial run covers up to 100 pages, 500 checks total. Rows beyond the first 4 are counted in `lockedCount` and unlock on paid runs. Trial data is kept 14 days after the trial ends. On free plans, runs bill per event from prepaid usage from run one (platform rule, not ours).

`Spend protections:` up to 200 pages per run — split larger portfolios across scheduled runs. Each run deep-checks a bounded number of below-MAP candidates; overflow flags `candidateCapHit` rather than billing unbounded work. `maxSpendUsd` (0.05–50, default 2) stops the run before it bills past your cap; a stopped run still publishes counts with `spendUsd` on the report. Raise `maxSpendUsd` and rerun to collect the evidence rows.

`Failure modes:` login-hidden pages yield `requiresAuth`. Each bills one `snapshot` and never a guessed advertised price. A currency mismatch against the baseline is treated as no change. It is never flagged. Oversize or unreachable pages skip for that URL with $0 charge. Failed fetches bill $0. If a run halts mid-charge, billed snapshots stand but nothing is lost: retry re-attempts only the remainder. Billed charges stand (no void API). Charge disputes go to Apify support within 15 days of the charge. Public `https://` pages only. `http://` is rejected at input. No screenshots in 0.1.0.

### Support

Report breakage via the Store discussion tab; replies and fixes stay visible there. Charge disputes go to Apify support within 15 days of the charge; no SLA.

### Machine access (no login)

Advertised-price checks only under your unilateral policy; verify-before-enforce.

- Store: https://apify.com/steadyworks/priced-proof
- Store API: https://api.apify.com/v2/acts/xeupuwWOQlX587MoH

[![Apify Store](https://img.shields.io/badge/Apify-Store-blue)](https://apify.com/steadyworks/priced-proof)

### Related

- https://apify.com/apify/web-scraper
- https://help.apify.com/en/articles/2644024-seo-for-actors

# Actor input Schema

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

Products to check: each advertised price is compared against its minimum advertised price under your unilateral policy. Structured-data shops (Shopify, WooCommerce, BigCommerce, custom) only, up to 200 per run.

## `authorizedSellers` (type: `array`):

Retailer hostnames excluded from advertised price violation and recovery rows (case-insensitive; exact-match — list subdomains explicitly, e.g. shop.example.com; trailing dots ignored). Snapshots still bill; verify-before-enforce applies.

## `alertMode` (type: `string`):

Choose violations-only (default) or include recoveries alongside advertised price violations. verify-before-enforce applies.

## `maxSpendUsd` (type: `number`):

Spend protection for each run: the run stops before billing past this cap, so a violation spike can never surprise you. Set it high to let large spikes through, low for tighter control. A stopped run still reports counts plus spendUsd; raise the cap and rerun to collect the full evidence rows. Per run only - schedules multiply it, so cap multi-run spend with console max-cost and schedule quotas. Honest edges: the per-run start event is a non-refundable compute reservation, including stopped runs; billed charges stand (no void API). A sustained price drop re-trips the cap every run until raised. Run 1 shows at most 4 preview rows (billed on paid, $0 on trial).

## `storeName` (type: `string`):

Stable portfolio key; same store name plus same targets reuses the advertised price baseline. Run 1 shows up to 4 current rows + lockedCount (billed on paid, $0 on trial); runs 2+ show changes.

## Actor input object example

```json
{
  "targets": [
    {
      "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
      "mapPrice": 60,
      "sku": "attic"
    },
    {
      "url": "https://books.toscrape.com/catalogue/sharp-objects_997/index.html",
      "mapPrice": 40,
      "sku": "sharp"
    }
  ],
  "alertMode": "violations-only",
  "maxSpendUsd": 2,
  "storeName": "demo-store"
}
```

# Actor output Schema

## `violations` (type: `string`):

Advertised prices below minimum advertised price with URL+timestamp+notice. Empty when compliant (counts-only on cap-trip). Capped at 4 rows on trial runs and paid run 1, remainder in report.lockedCount.

## `recoveries` (type: `string`):

SKUs returned to at-or-above minimum advertised price since last baseline. Only populated when input alertMode=violations-and-recoveries.

## `retailerSummary` (type: `string`):

One row per retailer hostname: complianceRate, violationCount, riskLevel (High/Medium/Low).

## `categorySummary` (type: `string`):

Single-row rollup: violationCount and mostViolatedSku (null when zero violations).

## `report` (type: `string`):

MAP\_COMPLIANCE\_REPORT from default key-value store: violations + summaries + recoveryCount, spendUsd, chargeId, blockCounts, allFail, baselineReset. Verify-before-enforce notice inside.

# 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 = {
    "targets": [
        {
            "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
            "mapPrice": 60,
            "sku": "attic"
        },
        {
            "url": "https://books.toscrape.com/catalogue/sharp-objects_997/index.html",
            "mapPrice": 40,
            "sku": "sharp"
        }
    ],
    "storeName": "demo-store"
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyworks/priced-proof").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 = {
    "targets": [
        {
            "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
            "mapPrice": 60,
            "sku": "attic",
        },
        {
            "url": "https://books.toscrape.com/catalogue/sharp-objects_997/index.html",
            "mapPrice": 40,
            "sku": "sharp",
        },
    ],
    "storeName": "demo-store",
}

# Run the Actor and wait for it to finish
run = client.actor("steadyworks/priced-proof").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 '{
  "targets": [
    {
      "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
      "mapPrice": 60,
      "sku": "attic"
    },
    {
      "url": "https://books.toscrape.com/catalogue/sharp-objects_997/index.html",
      "mapPrice": 40,
      "sku": "sharp"
    }
  ],
  "storeName": "demo-store"
}' |
apify call steadyworks/priced-proof --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyworks/priced-proof"
        }
    }
}
```

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/xeupuwWOQlX587MoH/builds/bCzd7SaAbRx7IJF78/openapi.json
