# CSV & Excel Diff — Rows, Fields and Schema Changes (`jaksondev/spreadsheet-diff`) Actor

Compare two CSV or Excel snapshots. Find added, removed and modified rows, field-level changes and schema differences. Validate unique row keys and expected row counts. Useful for inventory exports, product catalogs and data quality checks.

- **URL**: https://apify.com/jaksondev/spreadsheet-diff.md
- **Developed by:** [Jakson Lucas](https://apify.com/jaksondev) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$100.00 / 1,000 spreadsheet 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 & Excel Diff — Rows, Fields and Schema Changes

### What changed in your supplier price list?

Compare yesterday's supplier catalog with today's export before updating your inventory or storefront. Find changed prices, newly listed products and products missing from the latest file, matched by SKU. The same workflow works for inventory quantities and CRM exports when both files have a stable unique key.

**One completed comparison costs US$ 0.10, including up to 10,000 rows per input.** A daily comparison is about US$ 3 for 30 runs. Check the Pricing tab for the current price. No-change comparisons are charged too.

#### Automate a daily catalog check

1. Get yesterday's and today's complete CSV exports from your existing workflow. This Actor does not download your files or keep the previous snapshot for you.
2. Send their text as `previous` and `current`, with `format: "csv"` and the exact SKU column name in `keyColumn`.
3. Read `summary.hasChanges`. If false, finish. If true, inspect `modified`, `added` and `removed` before updating downstream systems.
4. Save today's original export in your own storage as tomorrow's baseline only after a successful comparison.

For n8n or Make, use an HTTP request step with the configuration below. This is a configuration recipe, not an installed or tested n8n template.

- Method: `POST`
- URL: `https://api.apify.com/v2/acts/jaksondev~spreadsheet-diff/run-sync-get-dataset-items?maxTotalChargeUsd=0.10&timeout=120`
- Authentication header: `Authorization: Bearer YOUR_APIFY_TOKEN` (store this in the workflow's credential manager).
- Content type: `application/json`
- JSON body: use the sample below, replacing the CSV strings with your workflow's actual exports.
- Response: an array containing one comparison report. Set the HTTP client timeout above 120 seconds and do not automatically retry uncertain failures: check the Apify run first to avoid a second paid comparison.

Use the source files' expected row counts if your export system provides them. A missing product in a partial export must not be mistaken for a real catalog removal.

Compare two complete spreadsheet snapshots and get a JSON report of added rows, removed rows, changed fields and schema changes. Match on a unique key such as SKU or ID to keep row reordering from producing false changes.

### Try the sample

Paste this input into the Actor:

```json
{
  "format": "csv",
  "previous": "sku,price\nA,10\nB,20\nC,30",
  "current": "sku,price\nD,40\nB,25\nA,10",
  "keyColumn": "sku",
  "expectedPreviousRows": 3,
  "expectedCurrentRows": 3
}
```

The report shows product D added, product C removed and product B's price changed from 20 to 25. Product A is unchanged even though its row moved. These are fictional sample products.

### Inputs

- `format`: `csv` (default) or `xlsx`. Both versions must use the same format.
- `previous` and `current`: CSV text, or base64-encoded XLSX file bytes. File URLs are not accepted in this version.
- `keyColumn`: optional exact column name. An explicit key must exist, be non-null and be unique in both files. Invalid explicit keys fail the run.
- `previousSheet` / `currentSheet`: exact sheet names, mandatory for workbooks with multiple sheets. Each run compares one selected sheet from each file.
- `expectedPreviousRows` / `expectedCurrentRows`: optional counts excluding headers and empty rows. Mismatches stop processing to help catch incomplete exports. Without these counts, a syntactically valid but incomplete export cannot be recognized automatically.

For XLSX, encode the bytes as base64 with your workflow's binary-to-base64 step. Do not paste a filename or a `data:` URL. The same parser and matching behavior apply to both formats.

### Output

The default dataset contains one report. Download JSON for the complete nested structure. It includes `summary`, `previousRowCount`, `currentRowCount`, `keyColumn` (when found), `added`, `removed`, `modified`, `schemaChanges`, `importantChanges`, `matching` and `warnings`.

An edited field is represented as:

```json
{"key":"B","fields":[{"column":"price","before":20,"after":25}]}
```

`summary.hasChanges` is false when normalized data is unchanged. A comparison with no changes is still a completed comparison.

If no reliable key is inferred, the Actor compares complete rows and preserves duplicate counts. In that mode an edited row appears as one removal and one addition, with an explicit warning. This version does not support composite keys or fuzzy matching.

### Limits and behavior

Each input is limited to 5 MiB decoded, 10,000 rows per sheet and 100 columns. The full output must fit within 8 MiB; oversized inputs or results fail rather than silently truncate. XLSX archive checks also apply. CSV must have a header and consistent records. Headers must be non-empty and unique. CSV encoding is UTF-8. Empty cells normalize to null; values, not formatting, are compared.

XLSX formulas need cached results. This Actor does not recalculate formulas, compare formatting, modify source files, keep historical snapshots between runs or identify corresponding products across different businesses.

### Billing

When pay-per-event monetization is enabled, one `comparison` event represents one completed comparison published to the result dataset, including unchanged results. Validation errors do not reach this event. The current price must be checked on the Pricing tab before running. Platform fees, if applicable to the selected pricing configuration, are shown by Apify.

Separate runs are separate comparisons. A run that already has its result does not publish another one on resume. Interruptions around storage/billing must be inspected in the platform records; do not assume that retrying a new run is free.

### Data and support

Inputs and outputs are processed in the Actor runtime and stored under the calling account's Apify storage configuration. The comparison code does not send spreadsheet contents to a language model or external analytics service. Manage input/output retention and sharing in your Apify account. Do not place private file contents in public issue reports.

For issues, provide the run ID and a small anonymized example through the Actor's Issues tab. Include the expected result and selected sheet/key. Do not publish credentials or private customer records.

# Actor input Schema

## `format` (type: `string`):

CSV uses plain text. XLSX uses base64-encoded file bytes.

## `previous` (type: `string`):

Paste CSV text, or base64 for XLSX.

## `current` (type: `string`):

Use a complete export, not a partial page of data.

## `keyColumn` (type: `string`):

Optional exact column name. Must be unique and non-empty in both inputs.

## `previousSheet` (type: `string`):

Required for workbooks containing multiple sheets.

## `currentSheet` (type: `string`):

Required for workbooks containing multiple sheets.

## `expectedPreviousRows` (type: `integer`):

Optional completeness check, excluding header and empty rows.

## `expectedCurrentRows` (type: `integer`):

Optional completeness check, excluding header and empty rows.

## Actor input object example

```json
{
  "format": "csv",
  "previous": "sku,price\nA,10\nB,20\nC,30",
  "current": "sku,price\nD,40\nB,25\nA,10"
}
```

# Actor output Schema

## `comparison` (type: `string`):

One JSON report containing added and removed rows, modified fields, schema changes, warnings and a summary.

# 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 = {
    "previous": `sku,price
A,10
B,20
C,30`,
    "current": `sku,price
D,40
B,25
A,10`
};

// Run the Actor and wait for it to finish
const run = await client.actor("jaksondev/spreadsheet-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 = {
    "previous": """sku,price
A,10
B,20
C,30""",
    "current": """sku,price
D,40
B,25
A,10""",
}

# Run the Actor and wait for it to finish
run = client.actor("jaksondev/spreadsheet-diff").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 '{
  "previous": "sku,price\\nA,10\\nB,20\\nC,30",
  "current": "sku,price\\nD,40\\nB,25\\nA,10"
}' |
apify call jaksondev/spreadsheet-diff --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jaksondev/spreadsheet-diff"
        }
    }
}

```

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/wR7IyT479tkQihTpH/builds/3a98ZBjuNag4vALJJ/openapi.json
