# Amazon Offer Snapshot Freshness Audit (`practical_ai_lab_jp/pal-amazon-offer-data-freshness-audit`) Actor

Offline audit of imported multi-seller Amazon offer snapshots: stale timestamps, mixed currencies, missing seller fields and per-ASIN comparison readiness.

- **URL**: https://apify.com/practical_ai_lab_jp/pal-amazon-offer-data-freshness-audit.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 $20.00 / 1,000 offer auditeds

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 Snapshot Freshness Audit

Check multi-seller Amazon offer snapshots **you already have** before you compare sellers. The Actor flags stale collection timestamps, mixed or ambiguous currencies, missing seller fields and duplicate rows, then tells you which ASINs are safe to compare.

It makes **no network requests** and does **not** scrape Amazon. It only reads the rows you paste or pipe into the input.

### Who it is for

Teams that import offer exports from several collection runs or tools and repeat a sourcing comparison (for example weekly). Mixing a three-day-old row with a fresh one, or USD with EUR, quietly breaks the comparison. This Actor catches that first.

### Input

Copy the prefilled input in the Console as a starting point.

| Field | Meaning | Default |
|---|---|---|
| `offers` | Array of offer objects (max 5000) | required |
| `referenceTime` | ISO 8601 time to measure age against; empty = now | now |
| `maxAgeHours` | Older rows are stale errors | 48 |
| `maxSpreadHours` | Max gap between oldest and newest usable offer per ASIN | 24 |
| `maxFutureMinutes` | Clock tolerance for future timestamps | 5 |
| `expectedCurrency` | Optional 3-letter code; others are errors | none |
| `requiredFields` | Fields every row must have | asin, seller, price, currency, collected_at |
| `minSellersPerAsin` | Usable distinct sellers needed per ASIN | 2 |

Recognised row keys (case and separators ignored): `asin`; `seller`, `sellerName`, `sellerId`, `merchant`, `soldBy`; `price`, `amount`; `currency`, `currencyCode`; `collectedAt`, `scrapedAt`, `fetchedAt`, `capturedAt`, `timestamp`; `marketplace`; `condition`. Timestamps may be ISO 8601 strings or epoch seconds/milliseconds. Timestamps without a timezone are assumed UTC and reported as info.

### Output

The dataset has two kinds of rows (use the `offers` and `groups` views):

- `offer` rows: `rowIndex`, `asin`, `seller`, `price`, `currency`, `collectedAt`, `ageHours`, `verdict` (`pass`/`warn`/`fail`), `issueCodes`, `issues`.
- `asin_group` rows: usable offer and seller counts, currencies, collection spread in hours, `comparisonReady`.

A `SUMMARY` record in the key-value store holds verdict counts, issue counts, settings used, the comparison-ready rate and caveats.

Issue codes: `missing_field`, `invalid_record`, `asin_format`, `invalid_price`, `currency_invalid`, `currency_symbol_only`, `currency_unrecognized`, `currency_from_price_text`, `currency_mismatch_expected`, `currency_mismatch_group`, `invalid_timestamp`, `timestamp_assumed_utc`, `stale_timestamp`, `future_timestamp`, `duplicate_offer`, plus group codes `mixed_currencies`, `timestamp_spread`, `too_few_usable_sellers`.

### Billing

The code emits one pay-per-event charge named `offer-audited` per audited row, only after the audit finished successfully. Invalid input or runtime errors end the run before any charge. The pricing itself is configured on the platform, not in this repository.

### Limitations

- It validates data hygiene only. It cannot verify that a price, seller or stock status is true or still live.
- Freshness is judged from timestamps in your data; wrong or reused timestamps pass unnoticed.
- Currency checks use 3-letter codes. Symbols like `$` or `¥` are ambiguous and only produce a warning; no exchange-rate conversion is done.
- The built-in currency list covers common codes; unlisted but well-formed codes get a warning.
- "Dominant currency" per ASIN is a majority vote, which is unreliable with 2–3 offers. Set `expectedCurrency` for small samples.
- `comparisonReady` is a rule-based signal, weak for ASINs with few offers. Defaults (48 h, 24 h, 2 sellers) are starting points, not benchmarks.
- Maximum 5000 rows per run; larger files must be split.
- Locale price parsing handles common `1,299.50` and `12,99` forms, not every regional format.

### Local tests

The tests are offline and use only the standard library: `python -m unittest discover -s tests -t .`

# Actor input Schema

## `offers` (type: `array`):

Offer rows as JSON objects (max 5000). Recognised keys: asin, seller (or sellerName/sellerId/merchant), price, currency, collectedAt (or scrapedAt/fetchedAt/timestamp), marketplace, condition.

## `referenceTime` (type: `string`):

Moment that snapshot age is measured against. Leave empty to use the current time.

## `maxAgeHours` (type: `integer`):

Snapshots older than this are flagged as stale errors.

## `maxSpreadHours` (type: `integer`):

Largest allowed gap between the oldest and newest usable offer for one ASIN before it is not comparison-ready.

## `maxFutureMinutes` (type: `integer`):

Timestamps later than the reference time by more than this are errors.

## `expectedCurrency` (type: `string`):

Optional 3-letter code such as USD. Offers in other currencies are flagged. Leave empty to only check consistency within each ASIN.

## `requiredFields` (type: `array`):

Fields that must be present on every row. Allowed: asin, seller, price, currency, collected_at, marketplace, condition.

## `minSellersPerAsin` (type: `integer`):

ASINs with fewer usable distinct sellers are not comparison-ready.

## Actor input object example

```json
{
  "offers": [
    {
      "asin": "B000000001",
      "seller": "Seller Alpha",
      "price": 24.99,
      "currency": "USD",
      "collectedAt": "2026-11-02T09:30:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000001",
      "seller": "Seller Beta",
      "price": "23.50",
      "currency": "USD",
      "collectedAt": "2026-10-27T08:00:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000001",
      "seller": "Seller Gamma",
      "price": 22.1,
      "currency": "EUR",
      "collectedAt": "2026-11-02T10:15:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "seller": "Seller Alpha",
      "price": "$18.00",
      "currency": "$",
      "collectedAt": "2026-11-02T11:00:00",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "price": 17.25,
      "currency": "USD",
      "collectedAt": "2026-11-02T11:20:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "seller": "Seller Delta",
      "price": 17.9,
      "currency": "USD",
      "collectedAt": "2026-11-02T11:40:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    }
  ],
  "referenceTime": "2026-11-02T12:00:00Z",
  "maxAgeHours": 48,
  "maxSpreadHours": 24,
  "maxFutureMinutes": 5,
  "expectedCurrency": "USD",
  "requiredFields": [
    "asin",
    "seller",
    "price",
    "currency",
    "collected_at"
  ],
  "minSellersPerAsin": 2
}
```

# 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 = {
    "offers": [
        {
            "asin": "B000000001",
            "seller": "Seller Alpha",
            "price": 24.99,
            "currency": "USD",
            "collectedAt": "2026-11-02T09:30:00Z",
            "marketplace": "amazon.com",
            "condition": "new"
        },
        {
            "asin": "B000000001",
            "seller": "Seller Beta",
            "price": "23.50",
            "currency": "USD",
            "collectedAt": "2026-10-27T08:00:00Z",
            "marketplace": "amazon.com",
            "condition": "new"
        },
        {
            "asin": "B000000001",
            "seller": "Seller Gamma",
            "price": 22.1,
            "currency": "EUR",
            "collectedAt": "2026-11-02T10:15:00Z",
            "marketplace": "amazon.com",
            "condition": "new"
        },
        {
            "asin": "B000000002",
            "seller": "Seller Alpha",
            "price": "$18.00",
            "currency": "$",
            "collectedAt": "2026-11-02T11:00:00",
            "marketplace": "amazon.com",
            "condition": "new"
        },
        {
            "asin": "B000000002",
            "price": 17.25,
            "currency": "USD",
            "collectedAt": "2026-11-02T11:20:00Z",
            "marketplace": "amazon.com",
            "condition": "new"
        },
        {
            "asin": "B000000002",
            "seller": "Seller Delta",
            "price": 17.9,
            "currency": "USD",
            "collectedAt": "2026-11-02T11:40:00Z",
            "marketplace": "amazon.com",
            "condition": "new"
        }
    ],
    "referenceTime": "2026-11-02T12:00:00Z",
    "expectedCurrency": "USD"
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ai_lab_jp/pal-amazon-offer-data-freshness-audit").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 = {
    "offers": [
        {
            "asin": "B000000001",
            "seller": "Seller Alpha",
            "price": 24.99,
            "currency": "USD",
            "collectedAt": "2026-11-02T09:30:00Z",
            "marketplace": "amazon.com",
            "condition": "new",
        },
        {
            "asin": "B000000001",
            "seller": "Seller Beta",
            "price": "23.50",
            "currency": "USD",
            "collectedAt": "2026-10-27T08:00:00Z",
            "marketplace": "amazon.com",
            "condition": "new",
        },
        {
            "asin": "B000000001",
            "seller": "Seller Gamma",
            "price": 22.1,
            "currency": "EUR",
            "collectedAt": "2026-11-02T10:15:00Z",
            "marketplace": "amazon.com",
            "condition": "new",
        },
        {
            "asin": "B000000002",
            "seller": "Seller Alpha",
            "price": "$18.00",
            "currency": "$",
            "collectedAt": "2026-11-02T11:00:00",
            "marketplace": "amazon.com",
            "condition": "new",
        },
        {
            "asin": "B000000002",
            "price": 17.25,
            "currency": "USD",
            "collectedAt": "2026-11-02T11:20:00Z",
            "marketplace": "amazon.com",
            "condition": "new",
        },
        {
            "asin": "B000000002",
            "seller": "Seller Delta",
            "price": 17.9,
            "currency": "USD",
            "collectedAt": "2026-11-02T11:40:00Z",
            "marketplace": "amazon.com",
            "condition": "new",
        },
    ],
    "referenceTime": "2026-11-02T12:00:00Z",
    "expectedCurrency": "USD",
}

# Run the Actor and wait for it to finish
run = client.actor("practical_ai_lab_jp/pal-amazon-offer-data-freshness-audit").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 '{
  "offers": [
    {
      "asin": "B000000001",
      "seller": "Seller Alpha",
      "price": 24.99,
      "currency": "USD",
      "collectedAt": "2026-11-02T09:30:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000001",
      "seller": "Seller Beta",
      "price": "23.50",
      "currency": "USD",
      "collectedAt": "2026-10-27T08:00:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000001",
      "seller": "Seller Gamma",
      "price": 22.1,
      "currency": "EUR",
      "collectedAt": "2026-11-02T10:15:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "seller": "Seller Alpha",
      "price": "$18.00",
      "currency": "$",
      "collectedAt": "2026-11-02T11:00:00",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "price": 17.25,
      "currency": "USD",
      "collectedAt": "2026-11-02T11:20:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    },
    {
      "asin": "B000000002",
      "seller": "Seller Delta",
      "price": 17.9,
      "currency": "USD",
      "collectedAt": "2026-11-02T11:40:00Z",
      "marketplace": "amazon.com",
      "condition": "new"
    }
  ],
  "referenceTime": "2026-11-02T12:00:00Z",
  "expectedCurrency": "USD"
}' |
apify call practical_ai_lab_jp/pal-amazon-offer-data-freshness-audit --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-data-freshness-audit"
        }
    }
}
```

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/RvhgDRpz5DUltZKUT/builds/RUR5LCxJTsypic3H3/openapi.json
