Dataset Diff & Alert Monitor avatar

Dataset Diff & Alert Monitor

Pricing

from $1.00 / 1,000 row compareds

Go to Apify Store
Dataset Diff & Alert Monitor

Dataset Diff & Alert Monitor

Run any Actor or read any dataset, diff it against the last run by key, and push added / changed / removed rows to a webhook, Telegram, Discord or Slack. Pay per row compared and per notification.

Pricing

from $1.00 / 1,000 row compareds

Rating

0.0

(0)

Developer

Matthew Curtis

Matthew Curtis

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 hours ago

Last modified

Share

Turn any Apify Actor or dataset into a change monitor. Run it on a schedule: it runs your source Actor (or reads a dataset you already have), compares the result with the last run by a key you choose, and sends the added, changed and removed rows to a webhook, Slack, Discord or Telegram. Every change is also written to the run's dataset, so n8n, Make, Zapier or your own code can pick it up.

No scraping of its own, no browser, no proxies. You pay per row compared and per message sent; the source Actor is billed at its own price.

What it is for

  • New job postings — run a LinkedIn, Indeed, Dice or Greenhouse jobs Actor daily and get only the new postings in Slack.
  • Price and stock watch — run a product scraper twice a week; watchFields: ["price", "inStock"] reports only those changes, with the previous value.
  • New tenders, permits, listings, grants — any registry scraper becomes an alert feed.
  • Review and rating watch — new reviews on Google Maps, Trustpilot or the app stores.
  • Compare two datasets once — pass previousDatasetId to diff any two datasets without a schedule.

How it works

  1. Source — set sourceActorId (username/actor-name) plus sourceInput, or sourceTaskId, or an existing datasetId.
  2. Key — keyFields identify an item across runs: ["url"], ["id"], ["title","company"]. Nested paths like product.sku work.
  3. Diff — items are hashed on their watched fields. ignoreFields drops fields that change every run (scrapedAt, rank); watchFields restricts changes to the fields you care about.
  4. Snapshot — the current state is saved in a named key-value store (snapshotStoreName, default dataset-diff-monitor) under snapshotKey. Leave the key empty and one is derived from the source and its input, so each schedule gets its own.
  5. Notify — a digest goes to every configured channel; the generic webhook gets a JSON body with the summary and the rows.

The first run stores the baseline silently (firstRunBehavior: "snapshot") or reports every item as added ("notify").

Input example

{
"sourceActorId": "curious_coder/linkedin-jobs-scraper",
"sourceInput": { "title": "data engineer", "location": "Chicago", "rows": 100 },
"keyFields": ["id"],
"ignoreFields": ["scrapedAt", "postedAt"],
"notifyOn": ["added"],
"labelField": "title",
"urlField": "link",
"slackWebhookUrl": "https://hooks.slack.com/services/…"
}

Schedule this Actor, not the source Actor: the monitor runs the source for you.

Worked examples

Each of these pairs the monitor with one of the author's source Actors; swap in any Store Actor the same way.

Alert you wantSource ActorMonitor input (beyond your channel)
New stories for a keyword, hourly — a Google Alerts you controlgoogle-news-scraper"sourceInput": {"queries": ["\"Acme Corp\""], "timeRange": "1d", "maxItems": 100}, "keyFields": ["id"], "notifyOn": ["added"], "labelField": "title", "urlField": "url"
A competitor app ships a version, moves rating or changes pricegoogle-play-app-scraper"sourceInput": {"queries": ["com.duolingo"], "locale": "en-US", "maxItems": 1}, "keyFields": ["id"], "watchFields": ["version", "score", "price", "updatedAt"], "notifyOn": ["changed"], "labelField": "title", "urlField": "url"
New or changed Washington DNR timber sales before the auctionwa-dnr-timber-sales"sourceInput": {"listings": ["upcoming", "unsold"]}, "keyFields": ["id"], "notifyOn": ["added", "changed"], "labelField": "saleName", "urlField": "packetUrl"
New Minnesota DNR auctions and posted resultsmn-dnr-timber-sales"sourceInput": {"includeCalendar": true}, "keyFields": ["id"], "notifyOn": ["added", "changed"], "labelField": "saleName", "urlField": "detailUrl"
New or repriced kleinanzeigen.de ads for a searchkleinanzeigen-scraper"sourceInput": {"queries": ["rennrad"], "location": "Köln", "fetchDetails": false, "maxItems": 100}, "keyFields": ["id"], "watchFields": ["price"], "notifyOn": ["added", "changed"], "labelField": "title", "urlField": "url"

Output

Dataset — one row per change the run reports:

{
"changeType": "changed",
"key": "https://example.com/item/42",
"label": "Item 42",
"url": "https://example.com/item/42",
"changedFields": ["price"],
"item": { "url": "https://example.com/item/42", "price": 19.99 },
"previous": { "url": "https://example.com/item/42", "price": 24.99 },
"detectedAt": "2026-10-24T19:47:51.899Z",
"snapshotKey": "snap-3f1c9a2b7e4d1c0a",
"sourceDatasetId": "…",
"sourceRunId": "…"
}

Key-value store OUTPUT — the run summary: counts, what it was compared against, which channels were notified, and the snapshot key.

Webhook body — { title, detectedAt, summary: {added, changed, removed, unchanged, compared}, added: [...], changed: [...], removed: [...], truncated }. Rows per type are capped at maxItemsPerMessage (default 20); the dataset always holds all of them.

Chat digest (Slack, Discord, Telegram):

Diff monitor: curious_coder/linkedin-jobs-scraper: 4 added, 2 changed, 3 removed (44 items compared)
ADDED (4)
• Senior Data Engineer https://…
…
CHANGED (2)
• Data Engineer II — salary https://…

Discord messages are split at 2,000 characters, Telegram at 4,000.

Pricing

Pay per event, no platform usage charges on top:

EventPrice
Actor start$0.02
Row compared (current items + removed items)$0.001
Notification message sent$0.01

A daily monitor over 500 items with one Slack digest costs about $0.53 per run plus the source Actor's own price. Reading an existing dataset instead of running a source Actor costs only the comparison.

Use from n8n, Make, MCP or code

  • n8n / Make — add the Apify node, pick this Actor, paste the input JSON. Or let the Actor call your workflow's webhook URL directly through webhookUrl.
  • MCP — https://mcp.apify.com/?tools=mdcurt/dataset-diff-monitor
  • API — POST https://api.apify.com/v2/acts/mdcurt~dataset-diff-monitor/runs?token=… with the input as the body.

Limits and notes

  • Items with no value in any key field are skipped and counted in OUTPUT.counts.skippedNoKey.
  • Duplicate keys keep the last item; the count is reported.
  • Snapshots above ~8 MB keep hashes only, so previous is empty in changed rows for very large datasets. Use watchFields to shrink what is compared.
  • A failing channel does not block the others; failures are listed in OUTPUT.notifications.failed.
  • dryRun: true computes and writes the diff but sends nothing and leaves the snapshot untouched.

Why this exists

Freelance boards in October 2026 kept asking for the same thing with different sources: a multi-site stock alert bot, a daily digest of CIO job postings across four boards, a procurement watcher that alerts on new solicitations. Each of those is a source Actor plus this monitor.