Detect Price & Stock Changes | Dataset Diff avatar

Detect Price & Stock Changes | Dataset Diff

Pricing

Pay per usage

Go to Apify Store
Detect Price & Stock Changes | Dataset Diff

Detect Price & Stock Changes | Dataset Diff

Spot price drops, stock changes, and catalog updates with Dataset Diff. Compare two JSON arrays or Apify datasets for field-level changes, before/after values, and numeric deltas. No scraping.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

panda studio

panda studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Spot price drops, stock changes, and catalog updates — compare two dataset snapshots and get exactly what changed as clean, field-level JSON. Point it at yesterday's scrape and today's scrape and it tells you which records were added, modified (with before / after values and numeric deltas), missing, and removed. Ideal for recurring e-commerce price monitoring and change detection — and it processes only user-supplied data, with no scraping.

Built for recurring monitoring: e-commerce price & availability tracking, catalog audits, lead-list change detection, and any workflow where you run a scraper on a schedule and only care about what moved.

  • No scraping, no browser, no anti-bot games. It only processes data you supply — inline JSON, or two Apify datasets your own token can read. Zero third-party Terms-of-Service risk.
  • Dependency-free & fast. Pure Python standard library. Typical runs finish in seconds.
  • Truthful by default. It won't call a record "removed" from a half-finished scrape — see Missing vs. removed below.

What you get

Every run writes one summary row plus one change row per affected record:

// summary row
{ "recordType": "summary", "oldCount": 1200, "newCount": 1198,
"added": 5, "modified": 41, "missing": 0, "removed": 7,
"unchanged": 1152, "evaluatedKeys": 1205, "errors": [] }
// a modified record
{ "recordType": "change", "changeType": "modified", "key": "sku-1",
"changes": [
{ "field": "price", "before": 19.99, "after": 17.99, "numericDelta": -2.0,
"beforePresent": true, "afterPresent": true }
] }
// an added record (full record included)
{ "recordType": "change", "changeType": "added", "key": "sku-4",
"record": { "id": "sku-4", "title": "Mechanical Keyboard", "price": 79.0 } }

Inputs

FieldTypeDefaultWhat it does
oldDatasetIdstringApify dataset ID of the previous snapshot. Your token must read it.
newDatasetIdstringApify dataset ID of the current snapshot.
oldItemsarrayPrevious snapshot as inline JSON (used when oldDatasetId is empty).
newItemsarrayCurrent snapshot as inline JSON.
keyFieldsarray["id"]Field(s) that identify a record across snapshots. Use several for a composite key (e.g. sku + variant).
compareFieldsarray[]Only check these fields. Empty = compare all fields except keys and ignored fields.
ignoreFieldsarray["scrapedAt","fetchedAt","crawledAt","#debug"]Volatile fields to skip so they don't create noise.
numericFieldsarray["price"]Fields compared as numbers; a numericDelta is computed and the tolerance applies. Currency symbols and thousands separators are tolerated ("$1,299.00"1299.0).
numericToleranceinteger0Ignore numeric changes at or below this absolute difference.
snapshotsCompletebooleanfalseSee Missing vs. removed.
includeUnchangedbooleanfalseAlso emit a row for records that didn't change.
maxRowsPerSnapshotinteger50000Safety cap; the run errors out instead of silently truncating.

Run it with no input and it compares a small built-in demo snapshot, so you can see the exact output shape before wiring up your own data.

Outputs

FieldPresent onMeaning
recordTypeallsummary or change.
changeTypechange rowsadded / modified / missing / removed / unchanged.
keychange rowsThe composite key of the record.
changesmodified rowsArray of {field, before, after, numericDelta, beforePresent, afterPresent}.
recordadded / missing / removed rowsThe full record.
oldCount,newCount,added,modified,missing,removed,unchanged,evaluatedKeyssummaryCounts.
errorssummaryNon-fatal issues (duplicate keys, missing key fields, skipped non-objects).

Missing vs. removed — why it matters

If a record is in the old snapshot but not the new one, that can mean two very different things:

  • The product was genuinely removed, or
  • Your new scrape was incomplete (a timeout, a blocked page, pagination that stopped early).

Treating an incomplete scrape as mass deletions is how monitoring pipelines send false "everything is gone" alerts. So by default this Actor labels those records missing (a soft signal). Set snapshotsComplete: true only when you are confident both snapshots are complete crawls — then they are labelled removed.


Example: daily price monitoring

  1. Schedule your product scraper to run every morning; each run produces a dataset.
  2. Schedule this Actor right after, with:
    • oldDatasetId = yesterday's dataset ID
    • newDatasetId = today's dataset ID
    • keyFields = ["sku"], numericFields = ["price"], numericTolerance = 0
  3. Wire the output to a webhook / integration. Every modified row with a price change and a negative numericDelta is a price drop worth acting on.

Notes

  • Matching is by exact key equality; pick keys that are stable across runs (an internal id or sku, not a position or a timestamp).
  • Records with duplicate keys keep the last occurrence and are reported in errors.
  • The Actor reads and writes datasets only through the documented Apify REST API and never logs your input values.