# Dataset Change Checker (`deomnus/dataset-change-checker`) Actor

Compare flat datasets with exact field changes, duplicate-key rejection, CSV output, and snapshot-size warnings for reliable automation workflows.

- **URL**: https://apify.com/deomnus/dataset-change-checker.md
- **Developed by:** [Deomnus VPG](https://apify.com/deomnus) (community)
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$250.00 / 1,000 completed comparisons

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

## Dataset Change Checker

Compare two flat JSON snapshots or two selected Apify datasets. Get added, removed and changed records, with exact old/new field values and checks for ambiguous inputs.

**US$0.25 per successfully completed comparison.** Invalid or incomplete comparisons do not trigger the product event.

Use this when a catalogue, inventory or listing workflow needs a repeatable comparison without maintaining its own matching and export code. If your existing script already handles these checks reliably, this tool may not add enough value for you.

### First comparison

In the Actor's JSON input editor, use:

```json
{
  "before": [
    { "sku": "A-10", "price": 12, "stock": 5 },
    { "sku": "B-20", "price": 25, "stock": 2 }
  ],
  "after": [
    { "sku": "A-10", "price": 14, "stock": 5 },
    { "sku": "C-30", "price": 30, "stock": 1 }
  ],
  "keyField": "sku"
}
```

Run with 512 MiB memory, a 60-second timeout and a maximum product charge of US$0.25. Wait for **SUCCEEDED**, then open **JSON summary and changes**, **CSV change records**, or the compact **Completed-comparison receipt** in the Output tab.

The example produces one added record (`C-30`), one removed record (`B-20`) and one changed record (`A-10`, price 12 to 14). A changed field includes presence flags:

```json
{
  "kind": "changed",
  "id": "A-10",
  "fields": [
    {
      "field": "price",
      "before": { "present": true, "value": 12 },
      "after": { "present": true, "value": 14 }
    }
  ]
}
```

### Workflow 1: compare existing Apify datasets

Select the old dataset under **Old dataset** and the new one under **New dataset**. Leave both inline-record properties absent. Set `keyField` to the stable identifier present in both snapshots. The Actor requests READ access only to the selected source datasets and never modifies them.

Your upstream jobs must finish writing before this comparison starts. Save their explicit dataset IDs; this tool does not guess which run is "yesterday" or automatically scrape a replacement. If either dataset changes while being read, the comparison fails instead of returning a partial result.

### Workflow 2: compare snapshots from another automation

Send `before`, `after` and `keyField` as JSON through the Apify API. Optionally send `compareFields`, for example `["price", "stock"]`, to ignore unrelated timestamps or description changes. Omit that property to compare all fields. Do not send an empty list.

Start one run and retain its run ID. Poll that run's status; do not start a new paid run merely because your HTTP client timed out. After SUCCEEDED, use the returned `defaultKeyValueStoreId` to retrieve `RESULT` and `CHANGES.csv`. A failed run may have partial files; file existence alone is not proof of successful billing or completion. The standard API is described in [Apify's run documentation](https://docs.apify.com/api/v2/act-runs-post).

### Comparison rules

- Record order does not matter. Matching is by one unique string or finite-number identifier.
- Identifiers must be present, non-null and nonempty; duplicates are errors. Numeric `1` and string `"1"` are different identifiers.
- Flat JSON values only: strings, finite numbers, booleans and null. Nested objects and arrays are rejected. Comparisons are exact and type-sensitive, with no rounding, trimming, fuzzy matching or coercion.
- Missing values differ from null. A missing value is represented by `{"present": false}`. A field absent from both complete snapshots is rejected if explicitly selected, helping catch misspellings.
- Changing an identifier is an addition plus a removal, not an update to the same record.
- At most 10,000 records across both snapshots, 5 MiB combined input and 8 MiB per output artifact. A comparison whose expanded output exceeds the cap is rejected.

### Warnings are not deletion instructions

In Apify Console, use `beforeEmpty: true` or `afterEmpty: true` to explicitly select an empty snapshot, with that side's records and dataset ID absent. Console can omit optional empty arrays, even from its JSON editor. An omitted source is always an error, never implicitly an empty snapshot. Direct JSON API inputs can still use `before: []` or `after: []`. Do not combine an empty flag with another source on the same side.

The tool warns if a previously populated snapshot becomes empty or its count falls by more than 50%. These warnings indicate possible incompleteness, not proof of an upstream failure. They do not validate business accuracy or authorize deleting downstream records. Inspect flagged runs before applying their changes. This Actor performs no downstream actions.

### Price

One **US$0.25** `comparison-completed` event after the JSON result and CSV are both saved. The event is attached to one compact default-dataset receipt so the completed output is visible and compatible with Apify's automated Store checks. A successful comparison with no changes is still one comparison. Invalid or incomplete comparisons produce no receipt and do not trigger this product event. There is no per-record or start charge, and platform usage is not passed through separately.

Your own source storage, retention, downloads or other platform services may still have charges under your Apify plan. Check the listing's actual pricing before starting a run. This Actor does not offer a refund policy beyond the applicable platform terms.

### Results, privacy and deletion

`RESULT` is the canonical JSON output. `CHANGES.csv` is a human-readable export with formula-prefix hardening, which can alter display text. Use JSON for lossless machine processing. The default dataset contains one compact receipt with counts, warnings and the two artifact names; it does not duplicate the source records or full change payload. `CHECKPOINT` holds progress metadata and an input fingerprint, not an extra copy of source records.

Input and results use Apify run storage; this tool has no separate database or payload telemetry. Application error messages use fixed codes rather than record values. Each limited-permission run executes under the customer's account and can access only its own storage plus resources explicitly selected in the input. The code does not copy run data to the developer.

Apify account settings can nevertheless let a customer opt in to sharing public-Actor runs with developers, and including a run URL in an Actor issue shares that run, its input, logs and default storage with the developer for support. Review the account's **Share run data with developers** setting and never attach a sensitive run to an issue. Platform processing, access controls and terms still apply.

Retention depends on the customer's plan. Apify currently retains a Free account's ten most recent runs and their storage for four months; unnamed runs beyond the most recent ten expire under the account retention rules. Paid accounts follow their configured retention, while named stores are retained indefinitely. Download needed results before expiry and inspect the current account settings; this Actor makes no fixed deletion-time promise. Use the key-value store's record/store deletion controls when appropriate. See [Apify retention rules](https://docs.apify.com/storage#data-retention) and [storage deletion controls](https://docs.apify.com/storage/key-value-store). Do not submit credentials, payment details or sensitive personal records as comparison data.

### Errors and support

| Code | Action |
|---|---|
| `DUPLICATE_IDENTIFIER`, `MISSING_IDENTIFIER`, `INVALID_IDENTIFIER` | Fix the key or upstream records; no automatic deduplication is performed. |
| `EXACTLY_ONE_SOURCE_PER_SIDE_REQUIRED` | Supply inline rows or a selected dataset for each side, not both. |
| `UNKNOWN_COMPARE_FIELD` | Correct the field name or omit `compareFields`. |
| `FLAT_JSON_RECORDS_REQUIRED` | Flatten nested data upstream. |
| `SOURCE_READ_FAILED`, `INCOMPLETE_SOURCE_READ`, `SOURCE_CHANGED_DURING_READ` | Check access and that the upstream snapshot finished successfully. |
| `RECORD_LIMIT_EXCEEDED`, `INPUT_SIZE_LIMIT_EXCEEDED`, `OUTPUT_SIZE_LIMIT_EXCEEDED` | Reduce input or selected fields; do not split snapshots in a way that changes matching semantics. |
| `INVALID_CUSTOMER_BUDGET`, `INSUFFICIENT_CUSTOMER_BUDGET` | Set an explicit finite product budget of at least US$0.25 for a paid comparison. |
| `BILLING_RECONCILIATION_REQUIRED`, `SAVED_RESULT_UNAVAILABLE` | Inspect the original run's charges and contact support before retrying. |

Use the Actor's Issues tab for support with the error code and a small synthetic reproduction. Never post API tokens, private datasets or financial information. Custom development, live onboarding, scraping, standalone CSV uploads and always-on monitoring are not included.

# Actor input Schema

## `before` (type: `array`):

Flat JSON records. Do not also select an old dataset.

## `after` (type: `array`):

Flat JSON records. Do not also select a new dataset.

## `beforeEmpty` (type: `boolean`):

Use this for an empty old snapshot in Console, which can remove empty arrays. Leave old records and old dataset absent.

## `afterEmpty` (type: `boolean`):

Use this for an empty new snapshot in Console. Leave new records and new dataset absent. A previously populated snapshot will produce an incompleteness warning.

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

Read-only source. Leave empty when old records are inline.

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

Read-only source. Leave empty when new records are inline.

## `keyField` (type: `string`):

One flat string or number field, unique within each snapshot. Missing/null/duplicate keys are rejected.

## `compareFields` (type: `array`):

Omit to compare all fields. Exact, type-sensitive values; missing is different from null.

## Actor input object example

```json
{
  "before": [
    {
      "sku": "DEMO-101",
      "price": 12.5,
      "stock": 8
    },
    {
      "sku": "DEMO-102",
      "price": 7,
      "stock": 0
    },
    {
      "sku": "DEMO-103",
      "price": 4,
      "stock": 5
    }
  ],
  "after": [
    {
      "sku": "DEMO-103",
      "price": 4,
      "stock": 5
    },
    {
      "sku": "DEMO-101",
      "price": 11,
      "stock": 6
    },
    {
      "sku": "DEMO-104",
      "price": 9,
      "stock": 2
    }
  ],
  "keyField": "sku"
}
```

# Actor output Schema

## `result` (type: `string`):

Canonical typed changes, counts and possible-incompleteness warnings.

## `csv` (type: `string`):

Spreadsheet-hardened human export; use JSON for lossless automation.

## `receipt` (type: `string`):

One compact dataset item with counts, warnings and artifact names. In paid mode this visible receipt is written with the single completion event.

# 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 = {
    "before": [
        {
            "sku": "DEMO-101",
            "price": 12.5,
            "stock": 8
        },
        {
            "sku": "DEMO-102",
            "price": 7,
            "stock": 0
        },
        {
            "sku": "DEMO-103",
            "price": 4,
            "stock": 5
        }
    ],
    "after": [
        {
            "sku": "DEMO-103",
            "price": 4,
            "stock": 5
        },
        {
            "sku": "DEMO-101",
            "price": 11,
            "stock": 6
        },
        {
            "sku": "DEMO-104",
            "price": 9,
            "stock": 2
        }
    ],
    "keyField": "sku"
};

// Run the Actor and wait for it to finish
const run = await client.actor("deomnus/dataset-change-checker").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 = {
    "before": [
        {
            "sku": "DEMO-101",
            "price": 12.5,
            "stock": 8,
        },
        {
            "sku": "DEMO-102",
            "price": 7,
            "stock": 0,
        },
        {
            "sku": "DEMO-103",
            "price": 4,
            "stock": 5,
        },
    ],
    "after": [
        {
            "sku": "DEMO-103",
            "price": 4,
            "stock": 5,
        },
        {
            "sku": "DEMO-101",
            "price": 11,
            "stock": 6,
        },
        {
            "sku": "DEMO-104",
            "price": 9,
            "stock": 2,
        },
    ],
    "keyField": "sku",
}

# Run the Actor and wait for it to finish
run = client.actor("deomnus/dataset-change-checker").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 '{
  "before": [
    {
      "sku": "DEMO-101",
      "price": 12.5,
      "stock": 8
    },
    {
      "sku": "DEMO-102",
      "price": 7,
      "stock": 0
    },
    {
      "sku": "DEMO-103",
      "price": 4,
      "stock": 5
    }
  ],
  "after": [
    {
      "sku": "DEMO-103",
      "price": 4,
      "stock": 5
    },
    {
      "sku": "DEMO-101",
      "price": 11,
      "stock": 6
    },
    {
      "sku": "DEMO-104",
      "price": 9,
      "stock": 2
    }
  ],
  "keyField": "sku"
}' |
apify call deomnus/dataset-change-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deomnus/dataset-change-checker"
        }
    }
}
```

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/zJmnepRUcsUsKJcoe/builds/sYAeVvbs2KNB4ewwF/openapi.json
