# CSV Diff & Per-Cell Change Report (`stellar_ballet_0bu/csv-diff`) Actor

Compare two CSV snapshots keyed on one or more columns and emit per-row added/removed/changed rows with a per-cell before/after view. Pairs with sitemap / price / product delta monitors that emit CSV-shaped snapshots.

- **URL**: https://apify.com/stellar\_ballet\_0bu/csv-diff.md
- **Developed by:** [Nikita S](https://apify.com/stellar_ballet_0bu) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## CSV Diff & Per-Cell Change Report

Compare two CSV snapshots keyed on one or more columns and emit per-row added/removed/changed rows with a per-cell before/after view. Pairs with the public `sitemap-delta-monitor`, `shopify-delta-monitor`, `schema-product-delta-monitor`, and other delta Actors that emit CSV-shaped snapshots.

- **Per-row output**:
  - `_kind: "added"` — present in `right` only, with `key` and `row`.
  - `_kind: "removed"` — present in `left` only, with `key` and `row`.
  - `_kind: "changed"` — present in both, with `key`, `cells[]` (column, before, after), plus `before` and `after` full rows.
  - `_kind: "unchanged"` — present in both with no tracked-cell differences.
- **Output** (`SUMMARY`): `counts` (left, right, added, removed, changed, unchanged), `leftName`/`rightName` labels, `key`, `ignoreColumns`.

### Quick start

```bash
apify call stellar_ballet_0bu/csv-diff -i '{
  "left":  { "name": "products_jul_1", "headers": ["sku","price","stock"],
             "rows": [{"sku":"A-1","price":"9.99","stock":"12"},
                      {"sku":"A-2","price":"14.50","stock":"0"},
                      {"sku":"A-3","price":"5.00","stock":"3"}] },
  "right": { "name": "products_jul_8", "headers": ["sku","price","stock"],
             "rows": [{"sku":"A-1","price":"9.99","stock":"8"},
                      {"sku":"A-2","price":"12.00","stock":"0"},
                      {"sku":"A-4","price":"7.00","stock":"5"}] },
  "key": ["sku"]
}'
````

The default dataset will have ~6 items (1 unchanged, 1 added, 1 removed, 3 changed-style or vice versa depending on direction). `SUMMARY.counts` returns `{left:3, right:3, added:1, removed:1, changed:1, unchanged:0}` in this example.

### Inputs

- `left` / `right` — provide either `text` (raw CSV) **or** `headers[]` + `rows[]` (array of objects). Optional `name` labels the side in the report.
- `key` — array of one or more column names that uniquely identify a row in both CSVs.
- `ignoreColumns` — columns to skip when computing per-cell diffs (e.g. timestamps, run ids). Defaults to none.
- `caseSensitiveKeys` — default false (case-insensitive).
- `maxChangesPerRow` — cap on per-row changed-cells array length (default 100).
- `outputFormat` — `rows` (default, one dataset item per diff result) or `table` (single dataset item with the full side-by-side table for small inputs).

### Use cases

- **Delta-monitor verification**: feed the previous and current dataset exports of any delta Actor in, get a precise added/removed/changed tally.
- **Migration QA**: did the new system preserve every row of the old CSV, and which fields drifted?
- **CRM/data-warehouse CDC** without a full ETL pipeline.
- **Price/stock reconciliation** between two Shopify/CSV exports.
- **Sitemap delta verification**: pair with the public `sitemap-delta-monitor` to assert that the expected URLs are present.

### API notes

- Pure data utility, no HTTP, no auth, no anti-bot, no scraping.
- Composite keys supported (`key: ["store", "sku"]`).
- Multiple rows on either side with the same composite key are compared in a cross-product (left × right) to surface all permutations.
- Identical values on a column are not included in the per-cell diff (`cells[]` only has the columns that actually changed).

### Pricing (Pay per event)

| Event | Description | USD |
|---|---|---|
| `apify-actor-start` | Built-in start charge, per Apify platform usage | per pricing tier |
| `row_diffed` | One charge per dataset row emitted (added + removed + changed). Unchanged rows are not charged. | $0.0001 |

`currentPricingInfo` activates on `2026-07-22T00:00:00Z`. The Actor's code calls `Actor.charge({ eventName, count })` only for meaningful diff rows; runs that produce no diffs are free aside from the start charge.

### Cost expectations

A 1k-row vs 1k-row diff with ~50 added/removed/changed rows will cost roughly $0.005 of usage credits on top of the standard start charge. Use `ignoreColumns` to suppress noisy timestamp/run-id cells and keep the dataset lean.

### FAQ

**Does it support `key: ["col1", "col2"]` composite keys?** Yes — any array of column names is treated as a composite key.

**What happens if the same key appears more than once on one side?** Pairs are compared in a cross-product; every left row with that key is compared to every right row with that key, surfacing all permutations.

**Why are unchanged rows in the output but not charged?** They are useful for downstream consumers that want to confirm "this key was processed and nothing changed". They cost nothing.

**Can I get a single side-by-side table item instead of one row per diff result?** Yes — set `outputFormat: "table"`. The default dataset will then have exactly one item with the full before/after table.

**Does it strip trailing whitespace or normalise line endings?** Yes — both `text` inputs are trimmed and CRLF/LF are normalised before parsing.

**Is there a hard cap on input size?** No hard cap, but each input row is materialised in memory. For multi-million-row diffs, run two `csv-dedupe-normalizer` passes first to shrink the inputs.

### Limits

- Best for inputs up to a few hundred thousand rows. Past that, split into shards.
- Per-row `cells[]` is capped at `maxChangesPerRow` (default 100). The full row is still emitted; only the explicit cell-level diff is truncated.

### Related Actors

- `shopify-delta-monitor` — emit a CSV-shaped snapshot of Shopify product deltas
- `schema-product-delta-monitor` — Schema.org JSON-LD price/availability deltas (CSV-shaped)
- `product-feed-delta-monitor` — Google Merchant / JSON / XML / CSV feed deltas
- `sitemap-delta-monitor` — public `sitemap.xml` URL deltas
- `csv-dedupe-normalizer` — pre-process to remove obvious duplicates before diffing
- `csv-join-enricher` — enrich a CSV with a second CSV by key (different use case but same family)

### Support

- Source: hidden (paid Actor pattern; not exposed by default).
- Issues / feature requests: open a thread on the public Apify Store Actor page (button on the right of the Store listing) or message the maintainer through the Apify Console.
- For paid-pilot bulk runs (multi-GB CSVs, scheduled reconciliation, custom output formats), contact the maintainer via the Apify Console.

### License

MIT-style Apify Actor Source-Available License. You may use the public Actor output and integrate the public results; you may not re-host the Actor source or re-publish it as a competing Actor.

# Actor input Schema

## `left` (type: `object`):

Baseline snapshot. Provide either `text` (raw CSV) or both `headers[]` and `rows[]` (objects). Optionally `name` to label it in the report.

## `right` (type: `object`):

Current snapshot. Same shape as `left`.

## `key` (type: `array`):

Column(s) that uniquely identify a row in both CSVs. Multiple columns form a composite key.

## `ignoreColumns` (type: `array`):

Columns to ignore when computing `changed` and per-cell diffs (e.g. timestamps, run ids).

## `caseSensitiveKeys` (type: `boolean`):

Treat key column values case-sensitively. Default false (case-insensitive).

## `maxChangesPerRow` (type: `integer`):

Cap the per-row changed-cells array length to avoid runaway output.

## Actor input object example

```json
{
  "key": [
    "id"
  ],
  "ignoreColumns": [],
  "caseSensitiveKeys": false,
  "maxChangesPerRow": 100
}
```

# Actor output Schema

## `diffRows` (type: `string`):

Default dataset items: one row per comparison with \_kind (added|removed|changed|unchanged), key, and (for changed) per-cell before/after.

## `summary` (type: `string`):

Run summary with ok, counts {left, right, added, removed, changed, unchanged}, leftName, rightName, key, ignoreColumns.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("stellar_ballet_0bu/csv-diff").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("stellar_ballet_0bu/csv-diff").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call stellar_ballet_0bu/csv-diff --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=stellar_ballet_0bu/csv-diff",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "CSV Diff & Per-Cell Change Report",
        "description": "Compare two CSV snapshots keyed on one or more columns and emit per-row added/removed/changed rows with a per-cell before/after view. Pairs with sitemap / price / product delta monitors that emit CSV-shaped snapshots.",
        "version": "0.2",
        "x-build-id": "gNKjNnyxL741MFJGQ"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/stellar_ballet_0bu~csv-diff/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-stellar_ballet_0bu-csv-diff",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/stellar_ballet_0bu~csv-diff/runs": {
            "post": {
                "operationId": "runs-sync-stellar_ballet_0bu-csv-diff",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/stellar_ballet_0bu~csv-diff/run-sync": {
            "post": {
                "operationId": "run-sync-stellar_ballet_0bu-csv-diff",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "left",
                    "right",
                    "key"
                ],
                "properties": {
                    "left": {
                        "title": "Left CSV (baseline)",
                        "type": "object",
                        "description": "Baseline snapshot. Provide either `text` (raw CSV) or both `headers[]` and `rows[]` (objects). Optionally `name` to label it in the report."
                    },
                    "right": {
                        "title": "Right CSV (current)",
                        "type": "object",
                        "description": "Current snapshot. Same shape as `left`."
                    },
                    "key": {
                        "title": "Key columns",
                        "type": "array",
                        "description": "Column(s) that uniquely identify a row in both CSVs. Multiple columns form a composite key.",
                        "default": [
                            "id"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "ignoreColumns": {
                        "title": "Ignore columns",
                        "type": "array",
                        "description": "Columns to ignore when computing `changed` and per-cell diffs (e.g. timestamps, run ids).",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "caseSensitiveKeys": {
                        "title": "Case-sensitive keys",
                        "type": "boolean",
                        "description": "Treat key column values case-sensitively. Default false (case-insensitive).",
                        "default": false
                    },
                    "maxChangesPerRow": {
                        "title": "Max changes per row",
                        "type": "integer",
                        "description": "Cap the per-row changed-cells array length to avoid runaway output.",
                        "default": 100
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
