# Product Price History & Deal Quality Scorer (`trovevault/deal-quality-scorer`) Actor

Track marketplaces and storefront product pages, build price history, detect fake discounts, and score whether deals are real, weak, or inflated.

- **URL**: https://apify.com/trovevault/deal-quality-scorer.md
- **Developed by:** [Trove Vault](https://apify.com/trovevault) (community)
- **Categories:** E-commerce, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.70 / 1,000 products

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Product Price History & Deal Quality Scorer

Track major marketplaces, common storefront platforms, and custom product pages, build reusable price history, detect fake discounts, and score whether a deal looks real, weak, or inflated. The actor compares the current price with the store's claimed reference price and the observed historical median, then returns a `dealQualityScore`, `referenceIntegrity`, `alertWorthy`, `confidence`, and a short plain-English reason.

It is built for Black Friday and Cyber Monday, but it stays useful year-round for deal media, shopping apps, affiliate publishers, competitor monitoring, price-alert workflows, and consumer trust layers.

The first run starts building history. Every later run gets smarter because the actor reuses a persistent Apify dataset of past observations.

---

### Why use Deal Quality Scorer?

Most price trackers stop at "this product is 30% off". That is not enough.

Users want to know:

- Is this actually a strong deal?
- Is the markdown real or just a raised compare-at price?
- Is the product near its usual low, or only slightly below normal?
- Should I alert users, publish this in a newsletter, or ignore it?

Deal Quality Scorer adds that missing layer:

- Builds reusable price history over time instead of judging deals in isolation
- Scores against observed pricing, not only against the page's marketing badge
- Flags likely inflated reference prices when claimed savings do not match historical behavior
- Returns user-facing output such as `dealLabel`, `referenceIntegrity`, `alertWorthy`, `recommendation`, and `reason`
- Supports Amazon, eBay, Target, Shopify, WooCommerce, and broader platform-specific plus generic product-page extraction paths

---

### What storefronts does it support?

#### Marketplaces
Direct support for Amazon, eBay, Target, and Walmart-specific patterns. Some marketplaces still benefit from proxy usage when they rate-limit or block direct requests.

#### Storefront platforms
Strongest support today is Shopify and WooCommerce. The actor also includes platform-aware extraction patterns for Magento, BigCommerce, PrestaShop, and Shopware.

#### Generic product pages
Everything else falls back to structured-data and selector-based extraction through:

- JSON-LD product markup
- meta price tags
- common visible price selectors
- optional custom CSS selectors you can provide in input

This means the actor is not limited to Shopify or WooCommerce. It is designed as a cross-store deal scorer, with selector overrides when a custom storefront uses non-standard markup.

---

### What does the actor return?

| Field | Description |
|---|---|
| `currentPrice` | Current observed selling price |
| `claimedReferencePrice` | Store-provided original, regular, compare-at, or list price |
| `claimedSavingsAmount` | Claimed absolute savings amount |
| `observedSavingsAmount` | Savings amount versus the observed historical median |
| `discountVsClaimedPrice` | Discount percent implied by the claimed reference price |
| `discountVsTypicalPrice` | Discount percent versus the historical median price |
| `historyMaturity` | `new`, `building`, `usable`, or `strong` |
| `dealQualityScore` | 0 to 100 score estimating how strong and trustworthy the deal is |
| `dealLabel` | Output label such as `strong_real_deal`, `weak_deal`, or `likely_fake_discount` |
| `referenceIntegrity` | Whether the claimed reference price looks `consistent`, `mixed`, `provisional`, or `suspect` |
| `pricePosition` | Whether the current price is `at_low`, `near_low`, `below_typical`, `typical`, or `above_typical` |
| `alertWorthy` | `true` when the deal looks strong enough to surface in alerts or editorial workflows |
| `confidence` | 0 to 1 confidence estimate based on extraction quality and history depth |
| `reason` | Short explanation of why the actor gave this score |

---

### Who pays for this?

#### Deal media and affiliate publishers
Filter out weak promotions before they hit your site, newsletter, or Telegram channel. Use `alertWorthy`, `dealLabel`, and `reason` to decide what deserves coverage.

#### Shopping apps and consumer trust layers
Add a trust score on top of raw scraped prices so users know whether a discount is worth acting on.

#### Competitor monitoring teams
Track how competitors price products over time and see whether their sale messaging reflects real discounting or not.

#### BFCM watchlists
Build history before peak sale periods, then score promotions when Black Friday and Cyber Monday arrive.

---

### Why this is better than a plain scraper

| Feature | Raw price scraper | Deal Quality Scorer |
|---|---|---|
| Current price | Yes | Yes |
| Claimed reference price | Sometimes | Yes |
| Persistent history across runs | Rarely | Yes |
| Compare against historical median | No | Yes |
| Flag likely fake markdowns | No | Yes |
| Alert-friendly output | No | Yes |
| Shopify support | Sometimes | Yes |
| WooCommerce support | Sometimes | Yes |
| Generic HTML fallback | Sometimes | Yes |
| Custom selectors for hard cases | Rarely | Yes |

---

### How it works

1. You provide one or more product URLs.
2. The actor detects the storefront and tries the cheapest structured extraction path first.
3. It reads prior observations from a persistent history dataset.
4. It scores the current deal against observed pricing behavior, not just the store's marketing.
5. It appends the new observation back into the history dataset unless it is a duplicate within the configured dedupe window.

Extraction priority:

1. Platform-specific lightweight extractors when available
2. Shopify product endpoint
3. WooCommerce Store API
4. JSON-LD product data
5. Meta tags and visible price selectors
6. Optional custom selectors from input

---

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `productUrls` | Array | required | Product-detail-page URLs to analyze |
| `historyDatasetName` | String | `deal-quality-history` | Named dataset used to persist observations across runs |
| `historyDatasetId` | String | optional | Existing dataset ID to use instead of the named history dataset |
| `historyLookbackDays` | Number | `365` | Number of days of history used for scoring |
| `minimumHistoryPoints` | Number | `3` | Minimum prior observations before scores are treated as mature |
| `dedupeWindowHours` | Number | `12` | Skip saving duplicate history rows inside this window |
| `persistObservation` | Boolean | `true` | Save the current observation back into the history dataset |
| `includeHistorySnapshot` | Boolean | `true` | Include recent observations in the output |
| `maxHistoryEntriesInOutput` | Number | `8` | Max entries inside `historySnapshot` |
| `platformHint` | String | `auto` | Force a known platform such as `amazon`, `target`, `shopify`, `woocommerce`, or `generic` |
| `enableHtmlFallback` | Boolean | `true` | Fall back to HTML extraction when APIs are not available |
| `titleSelector` | String | optional | Optional CSS selector for product title |
| `currentPriceSelector` | String | optional | Optional CSS selector for current price |
| `regularPriceSelector` | String | optional | Optional CSS selector for original or compare-at price |
| `currencySelector` | String | optional | Optional CSS selector for currency |
| `availabilitySelector` | String | optional | Optional CSS selector for stock status |
| `canonicalUrlSelector` | String | optional | Optional CSS selector for canonical URL |
| `proxyConfiguration` | Object | disabled | Enable only if the site blocks direct requests |
| `datasetId` | String | optional | Append scored rows to another dataset in addition to the run dataset |
| `runId` | String | optional | Pipeline or parent run ID |

Example input:

```json
{
    "productUrls": [
      {
        "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/"
      },
      {
        "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen"
      }
    ],
  "historyDatasetName": "deal-quality-history",
  "historyLookbackDays": 365,
  "minimumHistoryPoints": 3,
  "dedupeWindowHours": 12,
  "persistObservation": true,
  "includeHistorySnapshot": true,
  "platformHint": "auto",
  "enableHtmlFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
````

Example for a custom storefront with explicit selectors:

```json
{
  "productUrls": [
    {
      "url": "https://example-store.com/products/example-product",
      "label": "custom-store"
    }
  ],
  "currentPriceSelector": ".product-price .sale",
  "regularPriceSelector": ".product-price .compare",
  "currencySelector": ".product-price",
  "availabilitySelector": ".inventory-status"
}
```

***

### Output

Each product becomes one scored dataset row.

```json
{
  "recordType": "dealScore",
  "productUrl": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen",
  "platform": "shopify",
  "extractionMethod": "shopify_product_js",
  "priceSourceQuality": "api",
  "title": "Chocolate Hazelnut Klean Kanteen",
  "currency": "USD",
  "currentPrice": 17.5,
  "claimedReferencePrice": 25,
  "claimedSavingsAmount": 7.5,
  "observedSavingsAmount": 0,
  "discountVsClaimedPrice": 30,
  "discountVsTypicalPrice": 0,
  "historyMaturity": "building",
  "dealQualityScore": 43,
  "dealLabel": "provisional_discount",
  "referenceIntegrity": "provisional",
  "pricePosition": "at_low",
  "alertWorthy": false,
  "confidence": 0.46,
  "recommendation": "collect_more_history",
  "reason": "Store shows 30% off, but there are only 1 prior observations in the last 365 days. Current price $17.50 was saved so the score can mature over time."
}
```

***

### How to use the score

- `85-100`: Excellent real deal. Usually at or near the historical low and materially below normal pricing.
- `70-84`: Strong real deal. Good candidate for alerts, newsletters, affiliate coverage, or price-drop pushes.
- `55-69`: Decent deal. Real, but not unusually strong.
- `40-54`: Weak or provisional deal. Keep monitoring.
- `0-39`: Poor or misleading deal. Often above normal pricing or driven by a suspect reference price.

***

### Troubleshooting

#### The first runs are not very confident

That is expected. This actor improves over time. Keep `persistObservation` enabled and run it on a schedule so the history dataset becomes more informative.

#### A supported site still returns `PRICE_NOT_FOUND`

Leave `enableHtmlFallback` on and add `currentPriceSelector` plus `regularPriceSelector` if the page uses non-standard classes or variant widgets. This is the intended override path for custom HTML and edge-case storefront templates.

#### The actor reads the current price but not the original price

Some stores expose the live price but do not expose a usable compare-at price in HTML or structured data. The actor can still score against historical pricing, but fake-markup detection will be weaker.

#### Repeated runs are not adding new history rows

That is usually correct behavior. If the latest observation is identical within `dedupeWindowHours`, the actor skips writing a duplicate row.

#### A marketplace returns HTTP 403 or a robot check

Enable `proxyConfiguration` and try again. Some marketplaces and large retailers rate-limit direct traffic. Keep proxy usage selective, because the actor is designed to stay cheap by default.

#### Support

If you want stronger support for a specific storefront pattern, open an issue through the actor page and include:

- one product URL
- what you expected to extract
- what the actor returned

That is the fastest way to improve a new platform pattern.

***

### Limitations

- Shopify and WooCommerce are still the strongest structured-storefront paths in the current version.
- Marketplace support is expanding, but some sites may require proxy usage or storefront-specific tuning.
- Generic HTML support depends on the page exposing meaningful JSON-LD, meta tags, visible prices, or selectors you can provide.
- The actor deliberately stays on low-cost HTTP paths first. It does not use a browser crawler in this version.
- Very early history can surface strong signals, but mature fake-markup detection still benefits from repeated observations over time.

***

### Frequently asked questions

**Is this only for Black Friday and Cyber Monday?**\
No. BFCM is a strong use case, but the actor is useful year-round for shopping apps, affiliate sites, price alerts, and competitor intelligence.

**Does it work on non-Shopify sites?**\
Yes. It supports marketplaces, storefront platforms, and generic product pages. Shopify and WooCommerce are the strongest structured-storefront paths, but the actor is not limited to them.

**Can I force a known platform?**\
Yes. Use `platformHint` if you already know the storefront type and want to skip auto-detection.

**What happens on the first run?**\
The actor extracts the current price, saves it into the history dataset, and returns a provisional score if there is not enough prior history yet.

**Can I share one history dataset across many runs?**\
Yes. That is the default pattern. Use `historyDatasetName` to keep one rolling history dataset, or set `historyDatasetId` if you already manage one elsewhere.

**Can I integrate this into a larger workflow?**\
Yes. Use `datasetId` and `runId` to merge scored rows into downstream datasets or actor chains.

**Can I use this output for user alerts?**\
Yes. `alertWorthy`, `recommendation`, `dealLabel`, and `reason` are designed exactly for that.

**Does `likely_fake_discount` mean fraud?**\
No. It is a heuristic signal based on observed pricing behavior, not a legal conclusion.

***

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/trovevault~deal-quality-scorer/runs" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "productUrls": [
      { "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen" },
      { "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/" }
    ],
    "historyDatasetName": "deal-quality-history",
    "historyLookbackDays": 365,
    "minimumHistoryPoints": 3
  }'
```

***

### Related actors

- [Shopify Products Scraper](https://apify.com/trovevault/shopify-products-scraper): collect full Shopify catalog data cheaply
- [WooCommerce Products Scraper](https://apify.com/trovevault/woocommerce-products-scraper): collect full WooCommerce catalog data from Store API
- [E-Commerce Tech Stack Detector](https://apify.com/trovevault/e-commerce-tech-stack-detector): detect storefront technology before scraping

***

### Changelog

#### v0.3

- Added Amazon price-block extraction
- Added Target next-data extraction
- Expanded platform detection for Amazon, Walmart, eBay, Etsy, Target, Best Buy, Magento, BigCommerce, PrestaShop, and Shopware
- Added platform-specific selector packs for broader cross-store support
- Removed misleading example labels from input defaults and improved marketplace-focused troubleshooting

#### v0.2

- Added WooCommerce support through Store API
- Added generic HTML fallback with JSON-LD, meta tags, visible price selectors, and optional custom selectors
- Added `referenceIntegrity`, `pricePosition`, `claimedSavingsAmount`, `observedSavingsAmount`, `alertWorthy`, and `scoreDrivers`
- Improved multi-platform scoring and extraction diagnostics

#### v0.1

- Initial release with persistent price history, Shopify-first extraction, and deal scoring

***

### Feedback

If you want stronger support for a specific storefront type or want to turn this into a more editorial or alert-driven product, send a representative product URL through the actor page. That is the fastest way to make the next version more valuable.

# Actor input Schema

## `productUrls` (type: `array`):

One or more product page URLs to score. Use real product-detail-page URLs, not category or search pages. The actor supports major marketplaces, common storefront platforms, and generic product pages through structured data, platform-specific extractors, visible prices, and optional CSS selectors.

## `historyDatasetName` (type: `string`):

Name of the shared dataset that stores price observations across runs. Keep the same value over time to turn one-off checks into a reusable price-history product.

## `historyDatasetId` (type: `string`):

Existing dataset ID to use instead of the named history dataset. Best when another workflow already owns the history dataset for a client, category, or long-running monitor.

## `historyLookbackDays` (type: `integer`):

Number of days of stored observations to use when scoring each deal. Use 90 for campaign monitoring or 365 for stronger fake-markup detection.

## `minimumHistoryPoints` (type: `integer`):

Minimum number of prior observations before the actor treats a score as mature instead of provisional. Lower values make the actor faster to trust, but less conservative.

## `dedupeWindowHours` (type: `integer`):

If the latest saved observation for a product has the same current price and claimed reference price within this many hours, the actor skips writing a duplicate history row. This keeps the history dataset clean and cheap.

## `persistObservation` (type: `boolean`):

When enabled, each successful extraction is appended to the history dataset so future runs become smarter. Leave this on unless you only want a one-off QA run.

## `includeHistorySnapshot` (type: `boolean`):

Include a compact array of recent observations in each output row. Useful for QA, dashboards, audit trails, and downstream interfaces.

## `maxHistoryEntriesInOutput` (type: `integer`):

Maximum number of recent observations to include in the historySnapshot array when snapshots are enabled. Lower values keep outputs lighter.

## `platformHint` (type: `string`):

Force a platform if you already know the storefront type. Leave this on auto for mixed or unknown URLs so the actor can choose the cheapest working path.

## `enableHtmlFallback` (type: `boolean`):

If enabled, the actor falls back to HTML extraction using JSON-LD, meta tags, common price selectors, and optional custom selectors when platform-specific endpoints are unavailable.

## `titleSelector` (type: `string`):

Optional CSS selector for product title extraction on generic or custom storefronts. You can provide multiple selectors separated by commas or new lines.

## `currentPriceSelector` (type: `string`):

Optional CSS selector for the current live price. Use this when a custom storefront shows the price in HTML but not in JSON-LD or common classes.

## `regularPriceSelector` (type: `string`):

Optional CSS selector for the original, compare-at, list, or regular price. Use this to strengthen fake-markup detection on non-standard storefronts.

## `currencySelector` (type: `string`):

Optional CSS selector for currency codes or symbols on custom storefronts when the actor cannot infer currency cleanly.

## `availabilitySelector` (type: `string`):

Optional CSS selector for stock status, such as in stock or out of stock, on custom storefronts.

## `canonicalUrlSelector` (type: `string`):

Optional CSS selector for canonical product URLs on custom storefronts. Use this when multiple URLs point to the same product and you want cleaner history matching.

## `proxyConfiguration` (type: `object`):

Proxy settings for fetching product pages and lightweight APIs. Leave disabled for most runs to keep costs low. Enable Apify Proxy only when the target site blocks direct requests.

## `datasetId` (type: `string`):

Existing Apify dataset to append scored deal rows to in addition to the default run dataset. Useful for client pipelines or shared reporting datasets.

## `runId` (type: `string`):

Pipeline or parent run ID to carry through the output for downstream orchestration, joins, or QA tracing.

## Actor input object example

```json
{
  "productUrls": [
    {
      "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/"
    },
    {
      "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen"
    }
  ],
  "historyDatasetName": "deal-quality-history",
  "historyLookbackDays": 365,
  "minimumHistoryPoints": 3,
  "dedupeWindowHours": 12,
  "persistObservation": true,
  "includeHistorySnapshot": true,
  "maxHistoryEntriesInOutput": 8,
  "platformHint": "auto",
  "enableHtmlFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "productUrls": [
        {
            "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/"
        },
        {
            "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("trovevault/deal-quality-scorer").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 = { "productUrls": [
        { "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/" },
        { "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("trovevault/deal-quality-scorer").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "productUrls": [
    {
      "url": "https://www.amazon.com/XCHI-Watercolor-Fireworks-Independence-Decorations/dp/B0GT49Z3RC/"
    },
    {
      "url": "https://deathwishcoffee.com/products/chocolate-hazelnut-16oz-klean-kanteen"
    }
  ]
}' |
apify call trovevault/deal-quality-scorer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=trovevault/deal-quality-scorer",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Product Price History & Deal Quality Scorer",
        "description": "Track marketplaces and storefront product pages, build price history, detect fake discounts, and score whether deals are real, weak, or inflated.",
        "version": "0.3",
        "x-build-id": "pPZVmOt5Q7FJGpaUL"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/trovevault~deal-quality-scorer/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-trovevault-deal-quality-scorer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/trovevault~deal-quality-scorer/runs": {
            "post": {
                "operationId": "runs-sync-trovevault-deal-quality-scorer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/trovevault~deal-quality-scorer/run-sync": {
            "post": {
                "operationId": "run-sync-trovevault-deal-quality-scorer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "productUrls": {
                        "title": "Product URLs",
                        "type": "array",
                        "description": "One or more product page URLs to score. Use real product-detail-page URLs, not category or search pages. The actor supports major marketplaces, common storefront platforms, and generic product pages through structured data, platform-specific extractors, visible prices, and optional CSS selectors.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "historyDatasetName": {
                        "title": "History Dataset Name",
                        "type": "string",
                        "description": "Name of the shared dataset that stores price observations across runs. Keep the same value over time to turn one-off checks into a reusable price-history product.",
                        "default": "deal-quality-history"
                    },
                    "historyDatasetId": {
                        "title": "History Dataset ID (optional)",
                        "type": "string",
                        "description": "Existing dataset ID to use instead of the named history dataset. Best when another workflow already owns the history dataset for a client, category, or long-running monitor."
                    },
                    "historyLookbackDays": {
                        "title": "History Lookback Days",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Number of days of stored observations to use when scoring each deal. Use 90 for campaign monitoring or 365 for stronger fake-markup detection.",
                        "default": 365
                    },
                    "minimumHistoryPoints": {
                        "title": "Minimum History Points",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Minimum number of prior observations before the actor treats a score as mature instead of provisional. Lower values make the actor faster to trust, but less conservative.",
                        "default": 3
                    },
                    "dedupeWindowHours": {
                        "title": "Dedupe Window Hours",
                        "minimum": 1,
                        "type": "integer",
                        "description": "If the latest saved observation for a product has the same current price and claimed reference price within this many hours, the actor skips writing a duplicate history row. This keeps the history dataset clean and cheap.",
                        "default": 12
                    },
                    "persistObservation": {
                        "title": "Persist Observation",
                        "type": "boolean",
                        "description": "When enabled, each successful extraction is appended to the history dataset so future runs become smarter. Leave this on unless you only want a one-off QA run.",
                        "default": true
                    },
                    "includeHistorySnapshot": {
                        "title": "Include History Snapshot",
                        "type": "boolean",
                        "description": "Include a compact array of recent observations in each output row. Useful for QA, dashboards, audit trails, and downstream interfaces.",
                        "default": true
                    },
                    "maxHistoryEntriesInOutput": {
                        "title": "Max History Entries In Output",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of recent observations to include in the historySnapshot array when snapshots are enabled. Lower values keep outputs lighter.",
                        "default": 8
                    },
                    "platformHint": {
                        "title": "Platform Hint",
                        "enum": [
                            "auto",
                            "amazon",
                            "walmart",
                            "ebay",
                            "etsy",
                            "target",
                            "bestbuy",
                            "shopify",
                            "woocommerce",
                            "magento",
                            "bigcommerce",
                            "prestashop",
                            "shopware",
                            "generic"
                        ],
                        "type": "string",
                        "description": "Force a platform if you already know the storefront type. Leave this on auto for mixed or unknown URLs so the actor can choose the cheapest working path.",
                        "default": "auto"
                    },
                    "enableHtmlFallback": {
                        "title": "Enable HTML Fallback",
                        "type": "boolean",
                        "description": "If enabled, the actor falls back to HTML extraction using JSON-LD, meta tags, common price selectors, and optional custom selectors when platform-specific endpoints are unavailable.",
                        "default": true
                    },
                    "titleSelector": {
                        "title": "Title Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for product title extraction on generic or custom storefronts. You can provide multiple selectors separated by commas or new lines."
                    },
                    "currentPriceSelector": {
                        "title": "Current Price Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for the current live price. Use this when a custom storefront shows the price in HTML but not in JSON-LD or common classes."
                    },
                    "regularPriceSelector": {
                        "title": "Regular Price Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for the original, compare-at, list, or regular price. Use this to strengthen fake-markup detection on non-standard storefronts."
                    },
                    "currencySelector": {
                        "title": "Currency Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for currency codes or symbols on custom storefronts when the actor cannot infer currency cleanly."
                    },
                    "availabilitySelector": {
                        "title": "Availability Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for stock status, such as in stock or out of stock, on custom storefronts."
                    },
                    "canonicalUrlSelector": {
                        "title": "Canonical URL Selector (optional)",
                        "type": "string",
                        "description": "Optional CSS selector for canonical product URLs on custom storefronts. Use this when multiple URLs point to the same product and you want cleaner history matching."
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Proxy settings for fetching product pages and lightweight APIs. Leave disabled for most runs to keep costs low. Enable Apify Proxy only when the target site blocks direct requests.",
                        "default": {
                            "useApifyProxy": false
                        }
                    },
                    "datasetId": {
                        "title": "Dataset ID (optional)",
                        "type": "string",
                        "description": "Existing Apify dataset to append scored deal rows to in addition to the default run dataset. Useful for client pipelines or shared reporting datasets."
                    },
                    "runId": {
                        "title": "Run ID (optional)",
                        "type": "string",
                        "description": "Pipeline or parent run ID to carry through the output for downstream orchestration, joins, or QA tracing."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
