Dataset Diff & Change Detector
Pricing
from $5.00 / 1,000 difference detecteds
Dataset Diff & Change Detector
Returns what was added, removed or changed between two Apify datasets, files or Google Sheets by URL, or JSON arrays, matched by key, field by field, or since its own last run (snapshot mode). CSV/Excel diff report, named-dataset change log, webhook delivery. Agent-ready: pay per event (x402, MCP).
Pricing
from $5.00 / 1,000 difference detecteds
Rating
0.0
(0)
Developer
Adam Pearce
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
4 days ago
Last modified
Categories
Share
Compare two datasets and get exactly what changed, no manual spreadsheet comparison, no writing your own diff script. Point it at two Apify datasets, two CSV, Excel or JSON files or Google Sheets by URL, or two pasted JSON arrays (any mix), give it a key field like id, sku, or email, and it tells you precisely which rows were added, removed, or changed, down to which fields on each row actually moved. Works with the output of any scraper, any monitor Actor, or your own exported data. Or skip the 'old' side entirely: give the comparison a snapshot name and the Actor remembers the last dataset (or sheet) it saw, so every run reports what changed since the last run, appended to a running change log and POSTed to your webhook if you want.
Why use Dataset Diff & Change Detector?
If you scrape or export the same source more than once, you already have this problem: two big JSON files, and no fast way to answer "what actually changed since last time?" without eyeballing thousands of rows.
- Re-scraping the same source on a schedule? Feed it yesterday's dataset and today's, and skip building your own comparison logic.
- Reconciling two exports from different systems? Match them on a shared key (email, SKU, ID) and see exactly where they diverge.
- Auditing a data migration? Confirm nothing silently dropped or changed during a transfer.
- Want only what is new since yesterday's scrape, automatically? Set a snapshot name, chain this Actor after the scraper, and each run returns just the additions, removals and changes since the previous run, no dataset IDs to juggle.
- Watching a Google Sheet or a published CSV that other people edit? Put its link in New file URL with a snapshot name and schedule the run: every edit since last time lands in the dataset and, with a Webhook URL, in Slack.
- QA-checking a scraper you just changed? Diff its old output against its new output to see the real impact of your change, not just "does it run."
No scraping involved at all, it only processes data you already have, so there's nothing to break when a website changes and nothing to worry about on data-source terms.
How to use it
- Pick your two snapshots, each from any source: an existing Apify dataset (Old dataset / New dataset), a public file or Google Sheet link (Old file URL / New file URL), or a pasted JSON array (Old data (inline) / New data (inline)). Mixing is fine.
- Set Key field(s) to whatever uniquely identifies a row (e.g.
sku,id,email). This is what lets the Actor tell "this row changed" apart from "this row was removed and a different one was added". - Optionally list fields to ignore (like a
scrapedAttimestamp that always differs) under Fields to ignore, so it doesn't flag every row as changed for no real reason. - Pick where the result goes: this run's dataset (always), a named dataset that builds a running change log, a CSV or Excel report, or a webhook. Run it and check the added / removed / changed rows.
Bringing in a file or a Google Sheet
Set Old file URL and/or New file URL to any public link. The format is detected from the extension, the content type and the content itself, or force it with File format:
- CSV / TSV: header row required; quotes, embedded newlines and semicolon or tab delimiters are handled.
- Excel (.xlsx): the first sheet, header row in row 1; dates come out as ISO strings, formulas as their computed values.
- JSON: an array, an object wrapping an array (
{"items": [...]},{"data": [...]}), or one object per line (JSON Lines). - Google Sheets: paste the normal sheet link. Set sharing to "Anyone with the link can view" (or File > Share > Publish to the web); the Actor converts it to the CSV export link for you, including the specific tab if the link carries a
gid.
Up to 100 MB per side per run. Downloading a file is never charged. Values read from a CSV or sheet are text, so when the other side is a dataset with real numbers, list the numeric fields under Fields to ignore or compare like with like (two sheets, or two datasets) to avoid 19.99 vs "19.99" showing as a change.
Input
- Old dataset / New dataset: pick existing Apify datasets via the resource picker (limited-permissions safe, it can only read the dataset you point at).
- Old file URL / New file URL: a CSV, TSV, Excel, JSON or JSON Lines file, or a Google Sheet link, for either snapshot (see above). File format overrides auto-detection if ever needed.
- Old data / New data (inline): paste a JSON array directly instead, for one-off comparisons.
- Key field(s): the field(s) that identify a row across both snapshots. Leave empty to match by full row content instead (still works, but then a changed row shows as a remove+add pair, not a single "changed" row).
- Fields to compare: optionally restrict comparison to specific fields only.
- Fields to ignore: field names to exclude from comparison (timestamps, run IDs, anything that always differs).
- Include unchanged rows in the output: off by default, since most people only want the deltas.
- Also append to a named dataset: optional; every run's diff rows are also appended to a dataset of that name in your account (created on the first run), a running change log across scheduled runs. Not charged again.
- Export diff report as file: optionally generate a real downloadable CSV and/or Excel file on top of the dataset.
- Webhook URL: optional; POST the diff rows and counts to your endpoint on every run (see below).
- Snapshot name: optional. Name the comparison and the Actor keeps the 'new' snapshot under that name for next time, so you only ever supply the new data. See the next section.
Changes since the last run (snapshot mode)
Most people re-scrape the same source on a schedule and only care about the delta. Snapshot mode does that without you keeping track of dataset IDs:
- Set Snapshot name to something like
shop-prices. Leave Old dataset and Old data empty. - Point New dataset at today's dataset (or paste New data). Set Key field(s) as usual.
- Run it. The first run saves the baseline and reports nothing. Every later run with the same name compares against the saved snapshot, reports only the added, removed and changed rows, and then saves the new snapshot for next time.
To chain it after a scraper, add an integration on the scraper's Actor or task ("Run Actor" on finish) that starts this Actor with an input like:
{"snapshotName": "shop-prices","newDatasetId": "{{resource.defaultDatasetId}}","keyFields": ["sku"],"ignoreFields": ["scrapedAt"]}
Useful details:
- Seed the baseline without a wasted run: fill Old dataset or Old data on the first run together with the snapshot name. That run compares as normal and saves the new snapshot.
- A failed scrape cannot wipe your baseline: if the new snapshot has zero rows, nothing is compared, nothing is charged beyond the run start, and the saved snapshot is kept. To reset a name on purpose, run once with Reset snapshot turned on.
- Snapshots are stored gzipped in a key-value store named
nerolabs-dataset-diff-snapshotsin your own Apify account, created by this Actor, under the keys<name>__metaand<name>__part-0000,__part-0001, and so on. Delete those records, or use a new name, to start again. The Actor keeps its limited permissions: it only ever touches that store, its own default storages, and the datasets you point it at. - The
DIFF_SUMMARYrecord reports themode(two-snapshots,since-last-run,since-last-run-baselineorsince-last-run-skipped-empty) and, for snapshot runs, when the previous snapshot was saved and how many rows the new one holds.
Output
One row per difference, for example:
{"status": "changed","key": { "sku": "A100" },"oldValues": { "sku": "A100", "title": "Blue Widget", "price": 19.99, "stock": 42 },"newValues": { "sku": "A100", "title": "Blue Widget", "price": 17.99, "stock": 30 },"changedFields": ["price", "stock"]}
status is one of added, removed, changed, or (if you turned that option on) unchanged. You can download the dataset in various formats such as JSON, CSV, or Excel directly from the Output tab, or turn on the built-in export for a ready-to-open diff report file. With Also append to a named dataset set, the same rows are appended to a dataset of that name in your account, so a scheduled watch builds one running change log. The DIFF_SUMMARY record also says where each snapshot came from (dataset, fileUrl, inline or saved-snapshot).
Webhook destination
Set Webhook URL in the input and the diff rows (plus the added/removed/changed counts, the snapshot details and any download links) are POSTed there as JSON the instant the run finishes. The delivery happens on every run, including a first baseline run and a run with no changes, so a scheduled watch always reports back to Slack, Zapier, Make, n8n or your own API and your pipeline never wonders whether the run happened. A failed or unreachable webhook never breaks the run, it's reported as a warning in the output and costs nothing. Charged only on a confirmed delivery (see Pricing).
Pricing
Pay-per-event, anchored the same way as every low-maintenance data tool in this line: you're charged $0.005 per real difference found (an added, removed, or changed row), $0.0005 per unchanged row confirmed (only if you turn that option on), $0.01 per file export, and $0.02 per confirmed webhook delivery (only when your endpoint responds 2xx; a failed delivery costs nothing), effective 21 September 2026 and free before that. Comparing two 1,000-row datasets where 50 rows actually changed costs about $0.25, not $5, since you're only billed for the parts of the answer that are actually useful. Downloading a file by URL, saving a snapshot and appending to a named dataset are never charged. From 21 September 2026, Apify Store discounts apply on every event: 10% off for Bronze, 20% for Silver and 30% for Gold accounts. There's no separate platform-usage charge beyond the small per-GB run-start fee (the platform default).
Tips
- Always set Key field(s) if your data has any kind of stable identifier. Without one, the Actor still works, but "changed" rows show up as a remove+add pair instead, which is technically correct but less useful for spotting exactly what moved.
- Add any always-changing field (timestamps, scrape IDs, "last checked" dates) to Fields to ignore, otherwise every single row will look "changed" every time.
- Duplicate key values within one snapshot are handled gracefully (the last-seen row wins) and flagged in the run's warnings, so you'll know if your chosen key field isn't actually unique.
Works with the rest of the Nero Labs dataset toolkit
- Dataset Cleaner & Exporter: dedupe (exact, normalized or fuzzy), flatten nested JSON, clean emails, phones and URLs, then export CSV or Excel.
- Dataset Filter & Transform: keep the rows you want and reshape the fields (dates, replace, split, hash, 25 ops), sort, dedupe, limit.
- Dataset Join & Merge: VLOOKUP-style joins and unions across two datasets, files or Google Sheets on a key field.
- Dataset Aggregate, Group By & Pivot: counts, sums, averages and pivot tables per group.
- Dataset Diff & Change Detector (this one): what was added, removed or changed since last time.
- Dataset AI Enrich: add LLM-generated columns (classify, extract, summarise) to every row, no API key needed.
- Dataset Charts & Report: chart images (PNG, SVG) and a PDF or HTML report from any data.
- Dataset to Postgres, Supabase & MySQL: write the rows straight into a database table, creating it if needed.
- Dataset to REST API: send every row to any API as its own request, with templating and auth presets.
- Actor Pipeline Runner: chain several of these together in one run, each step fed the previous step's dataset.
A common pipeline: a scraper, then Cleaner, then Filter & Transform, then Join to enrich from a sheet, then Aggregate for the weekly summary, with Diff watching what changed and Charts & Report turning the numbers into the Monday PDF. Pipeline Runner runs that whole chain in one call.
FAQ
Does this work with any Apify dataset? Yes, point "Old dataset" and "New dataset" at any two dataset IDs from your own account, including the output of another Actor's run.
How do I get only what changed since my last scrape, without saving dataset IDs? Set a Snapshot name. The Actor keeps the last snapshot for you and each run reports the delta since the previous one. See "Changes since the last run" above.
Where are the snapshots kept and can I delete them? In a key-value store called nerolabs-dataset-diff-snapshots in your own account, one __meta record plus gzipped __part-NNNN records per name. Delete them any time, or start fresh with a new name.
What if I don't have two dataset IDs, just two JSON files? Use the inline "Old data" / "New data" fields instead and paste the arrays directly.
Can I watch a Google Sheet for changes? Yes. Paste the sheet link into New file URL, set a Snapshot name and put the Actor on a schedule; each run reports what changed in the sheet since the previous run. If the link gives an HTTP 401 or 403, the sheet isn't public: set sharing to "Anyone with the link can view", or use File > Share > Publish to the web and paste that CSV link.
Is my data safe? This Actor only reads the sources you point it at and writes to your own Apify account's storage (the run's output, its snapshot store, and a named dataset if you set one). It doesn't scrape and doesn't retain anything else. The one place data leaves your account is a Webhook URL you set yourself, which receives only that run's result.
Found a bug or want a feature? Use the Issues tab, replies come from a real person, usually within hours.