# Amazon Offer Price History Diff (`practical_ai_lab_jp/pal-amazon-offer-price-history-diff`) Actor

Compare two dated Amazon product datasets you already have: price, list price, availability and seller changes, same-currency only.

- **URL**: https://apify.com/practical_ai_lab_jp/pal-amazon-offer-price-history-diff.md
- **Developed by:** [Practical AI Lab](https://apify.com/practical_ai_lab_jp) (community)
- **Categories:** E-commerce, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 diff rows

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 Offer Price History Diff

Compare two dated snapshots of Amazon product data **that you already have** and get a row-per-ASIN report of what changed: price, list price, discount depth, availability and seller.

This Actor never visits Amazon, logs in anywhere or fetches external pages. It only reads the data you give it (inline JSON or an Apify dataset in your own account).

### What makes it different from a scraper

- It is an analysis step, not a data collector. Pair it with any export you are entitled to use.
- **Comparable currency only.** A price delta is computed only when both snapshots carry the same known ISO currency code. A EUR price versus a USD price gets a `currency_mismatch` status and no number, instead of a misleading difference. No exchange rates are applied.
- Ambiguous symbols (`$`, `¥`) are never guessed. Supply a `currency` field, a code in the text such as `USD 19.99`, or set `defaultCurrency` if you are certain.
- Range prices such as `$10 - $20` are not parsed.

### Input

| Field | Purpose |
| --- | --- |
| `beforeDate`, `afterDate` | Collection dates (YYYY-MM-DD) of the two snapshots. Required. |
| `beforeItems`, `afterItems` | Snapshot items as JSON, or use the dataset IDs below. |
| `beforeDatasetId`, `afterDatasetId` | Datasets in your own Apify account (alternative to JSON). |
| `targetCurrency` | Only compute deltas in this currency. |
| `defaultCurrency` | Assumed currency for records with none. |
| `minChangePercent` | Ignore smaller price moves. |
| `includeUnchanged` | Also output matched ASINs with no change. |
| `includeAddedRemoved` | Output ASINs present in only one snapshot. |
| `maxRows` | Cap on stored rows (default 5000). |

Recognized item fields (first match wins): `asin` or an Amazon product `url` containing `/dp/<ASIN>`; `title`/`name`; `price`/`currentPrice` (number, text, or `{value, currency}`); `currency`; `listPrice`/`originalPrice`; `availability`/`inStock`; `seller`/`soldBy`; `marketplace`/`domain` (otherwise derived from the URL host).

The prefilled sample input can be copied as is.

### Output

Dataset rows contain `asin`, `marketplace`, `comparison` (`matched`, `added`, `removed`), `changes` (for example `price_drop`, `price_increase`, `list_price_up`, `list_price_down`, `became_unavailable`, `back_in_stock`, `seller_changed`), before/after prices with currencies, `priceDelta`, `pricePctChange`, `priceStatus`, the same for list price, discount percentages (only when price and list price share a currency), availability and seller.

A run summary is stored in the key-value store record `OUTPUT_SUMMARY`, including counts, change counts and caveats.

### Pricing model

The code emits one pay-per-event event named `diff-row` per stored output row, only after rows were stored successfully. Runs that fail validation or crash store and charge nothing. Billing configuration is done separately on the platform and is not part of this repository.

### Limitations and caveats

- Only two points in time are compared. Moves in between are invisible; this is not continuous history.
- Output quality equals input quality. The Actor cannot verify that your data is correct, complete or really from the stated dates.
- Matching is by marketplace plus ASIN. If records lack a marketplace and URL, the same ASIN from different marketplaces is treated as one product. The first record per key is kept; duplicates are counted in the summary.
- Seller changes are flagged only when both snapshots name a seller. Availability changes only when both sides are recognizable (`in_stock` or `out_of_stock`).
- Number parsing heuristics: a single separator followed by exactly three digits (for example `1,299`) is read as a thousands separator.
- With small samples (fewer than 30 matched ASINs) the summary says so; do not treat shares or averages from such samples as representative.
- Check that you are permitted to use the data you supply, and that it contains no personal data.
- The earlier snapshot is indexed in memory; very large snapshots (hundreds of thousands of ASINs) need more memory.

### Local tests

The unit tests are offline: `python -m unittest discover tests`.

# Actor input Schema

## `beforeDate` (type: `string`):

Date the earlier dataset was collected, YYYY-MM-DD.

## `afterDate` (type: `string`):

Date the later dataset was collected, YYYY-MM-DD. Must not be earlier than the earlier date.

## `beforeItems` (type: `array`):

Items of the earlier snapshot. Recognized fields: asin or url, title, price (number, text or {value, currency}), currency, listPrice, availability or inStock, seller, marketplace. Leave empty if you use a dataset ID.

## `afterItems` (type: `array`):

Items of the later snapshot, same format as the earlier one. Leave empty if you use a dataset ID.

## `beforeDatasetId` (type: `string`):

ID of an Apify dataset in your account holding the earlier snapshot. Use instead of the JSON items.

## `afterDatasetId` (type: `string`):

ID of an Apify dataset in your account holding the later snapshot. Use instead of the JSON items.

## `targetCurrency` (type: `string`):

If set, price deltas are computed only for this ISO currency code (for example USD). Other currencies are reported without a delta.

## `defaultCurrency` (type: `string`):

Only set this if you are certain all currency-less prices share one currency. Symbols like $ and ¥ are ambiguous and are never guessed.

## `minChangePercent` (type: `number`):

Price or list price moves smaller than this percentage are treated as unchanged.

## `includeUnchanged` (type: `boolean`):

Also output matched ASINs with no detected change. These rows are billable output rows.

## `includeAddedRemoved` (type: `boolean`):

Output ASINs that appear in only one of the two snapshots.

## `maxRows` (type: `integer`):

Upper bound on stored rows, which also bounds cost.

## Actor input object example

```json
{
  "beforeDate": "2026-09-01",
  "afterDate": "2026-10-01",
  "beforeItems": [
    {
      "asin": "B000000001",
      "title": "Sample kettle",
      "price": {
        "value": 39.99,
        "currency": "USD"
      },
      "listPrice": {
        "value": 49.99,
        "currency": "USD"
      },
      "availability": "In Stock",
      "seller": "Example Seller A"
    },
    {
      "asin": "B000000002",
      "title": "Sample lamp",
      "price": "EUR 24.50",
      "availability": "In Stock",
      "seller": "Example Seller B"
    },
    {
      "asin": "B000000003",
      "title": "Sample bottle",
      "price": 12,
      "currency": "USD",
      "inStock": true,
      "seller": "Example Seller A"
    }
  ],
  "afterItems": [
    {
      "asin": "B000000001",
      "title": "Sample kettle",
      "price": {
        "value": 34.99,
        "currency": "USD"
      },
      "listPrice": {
        "value": 49.99,
        "currency": "USD"
      },
      "availability": "In Stock",
      "seller": "Example Seller C"
    },
    {
      "asin": "B000000002",
      "title": "Sample lamp",
      "price": "USD 26.00",
      "availability": "Currently unavailable",
      "seller": "Example Seller B"
    },
    {
      "asin": "B000000004",
      "title": "Sample mug",
      "price": 8.5,
      "currency": "USD",
      "inStock": true,
      "seller": "Example Seller D"
    }
  ],
  "targetCurrency": "",
  "defaultCurrency": "",
  "minChangePercent": 0,
  "includeUnchanged": false,
  "includeAddedRemoved": true,
  "maxRows": 5000
}
```

# Actor output Schema

## `results` (type: `string`):

Download rows from the latest run in JSON/CSV.

# 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 = {
    "beforeDate": "2026-09-01",
    "afterDate": "2026-10-01",
    "beforeItems": [
        {
            "asin": "B000000001",
            "title": "Sample kettle",
            "price": {
                "value": 39.99,
                "currency": "USD"
            },
            "listPrice": {
                "value": 49.99,
                "currency": "USD"
            },
            "availability": "In Stock",
            "seller": "Example Seller A"
        },
        {
            "asin": "B000000002",
            "title": "Sample lamp",
            "price": "EUR 24.50",
            "availability": "In Stock",
            "seller": "Example Seller B"
        },
        {
            "asin": "B000000003",
            "title": "Sample bottle",
            "price": 12,
            "currency": "USD",
            "inStock": true,
            "seller": "Example Seller A"
        }
    ],
    "afterItems": [
        {
            "asin": "B000000001",
            "title": "Sample kettle",
            "price": {
                "value": 34.99,
                "currency": "USD"
            },
            "listPrice": {
                "value": 49.99,
                "currency": "USD"
            },
            "availability": "In Stock",
            "seller": "Example Seller C"
        },
        {
            "asin": "B000000002",
            "title": "Sample lamp",
            "price": "USD 26.00",
            "availability": "Currently unavailable",
            "seller": "Example Seller B"
        },
        {
            "asin": "B000000004",
            "title": "Sample mug",
            "price": 8.5,
            "currency": "USD",
            "inStock": true,
            "seller": "Example Seller D"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ai_lab_jp/pal-amazon-offer-price-history-diff").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 = {
    "beforeDate": "2026-09-01",
    "afterDate": "2026-10-01",
    "beforeItems": [
        {
            "asin": "B000000001",
            "title": "Sample kettle",
            "price": {
                "value": 39.99,
                "currency": "USD",
            },
            "listPrice": {
                "value": 49.99,
                "currency": "USD",
            },
            "availability": "In Stock",
            "seller": "Example Seller A",
        },
        {
            "asin": "B000000002",
            "title": "Sample lamp",
            "price": "EUR 24.50",
            "availability": "In Stock",
            "seller": "Example Seller B",
        },
        {
            "asin": "B000000003",
            "title": "Sample bottle",
            "price": 12,
            "currency": "USD",
            "inStock": True,
            "seller": "Example Seller A",
        },
    ],
    "afterItems": [
        {
            "asin": "B000000001",
            "title": "Sample kettle",
            "price": {
                "value": 34.99,
                "currency": "USD",
            },
            "listPrice": {
                "value": 49.99,
                "currency": "USD",
            },
            "availability": "In Stock",
            "seller": "Example Seller C",
        },
        {
            "asin": "B000000002",
            "title": "Sample lamp",
            "price": "USD 26.00",
            "availability": "Currently unavailable",
            "seller": "Example Seller B",
        },
        {
            "asin": "B000000004",
            "title": "Sample mug",
            "price": 8.5,
            "currency": "USD",
            "inStock": True,
            "seller": "Example Seller D",
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("practical_ai_lab_jp/pal-amazon-offer-price-history-diff").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 '{
  "beforeDate": "2026-09-01",
  "afterDate": "2026-10-01",
  "beforeItems": [
    {
      "asin": "B000000001",
      "title": "Sample kettle",
      "price": {
        "value": 39.99,
        "currency": "USD"
      },
      "listPrice": {
        "value": 49.99,
        "currency": "USD"
      },
      "availability": "In Stock",
      "seller": "Example Seller A"
    },
    {
      "asin": "B000000002",
      "title": "Sample lamp",
      "price": "EUR 24.50",
      "availability": "In Stock",
      "seller": "Example Seller B"
    },
    {
      "asin": "B000000003",
      "title": "Sample bottle",
      "price": 12,
      "currency": "USD",
      "inStock": true,
      "seller": "Example Seller A"
    }
  ],
  "afterItems": [
    {
      "asin": "B000000001",
      "title": "Sample kettle",
      "price": {
        "value": 34.99,
        "currency": "USD"
      },
      "listPrice": {
        "value": 49.99,
        "currency": "USD"
      },
      "availability": "In Stock",
      "seller": "Example Seller C"
    },
    {
      "asin": "B000000002",
      "title": "Sample lamp",
      "price": "USD 26.00",
      "availability": "Currently unavailable",
      "seller": "Example Seller B"
    },
    {
      "asin": "B000000004",
      "title": "Sample mug",
      "price": 8.5,
      "currency": "USD",
      "inStock": true,
      "seller": "Example Seller D"
    }
  ]
}' |
apify call practical_ai_lab_jp/pal-amazon-offer-price-history-diff --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,practical_ai_lab_jp/pal-amazon-offer-price-history-diff"
        }
    }
}
```

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/WLCP3YwCRMRE9K8eY/builds/1Ibh58adfpayhajx2/openapi.json
