---
url: https://findutils.com/guides/csv-diff
title: "Compare Two CSV Files by ID: Find Added, Removed and Changed Rows"
description: "Compare two CSV or Excel exports by a key column such as id or email and list the rows that were added, removed or changed, cell by cell. Runs in your browser."
category: converters
content_type: guide
guide_type: subtopic
cluster: data-conversion
locale: en
read_time: 7
status: published
author: "olgunozoktas"
published_at: 2026-09-26T12:00:00Z
excerpt: "Two exports of the same table, a week apart, and the question is what changed. A line diff drowns in reordered rows. Matching the rows by a key column and comparing them cell by cell answers it directly: these rows are new, these are gone, and these three prices moved."
tag_ids: ["csv", "diff", "spreadsheet", "data-conversion"]
tags: ["CSV", "Diff", "Spreadsheet", "Data Conversion"]
primary_keyword: "compare two csv files"
secondary_keywords: ["csv diff", "compare two excel exports by id", "find changed rows between two csv files", "compare csv by key column", "tsv diff"]
tool_tag: "csv-diff"
related_tool: "csv-diff"
related_tools: ["csv-diff", "csv-dedupe", "csv-merger", "csv-viewer", "diff-checker", "json-diff"]
og_image: "/images/content/guides/csv-diff-cover-20260926.webp"
image_alt: "Two stacks of blank grey tiles on a desk; in the right stack two tiles glow amber, one gap is outlined in red, and an extra green tile sits beside it."
updated_at: "2026-09-26T12:00:00Z"
---

To compare two CSV files, match their rows by a key column such as `id`, `email` or `sku`, then compare the other cells of each matched pair. FindUtils [CSV Diff](/convert/csv-diff/) does this in your browser: it lists every row as added, removed or changed, names the cells that changed with their old and new values, and gives you the result as a diff CSV. Rows that only moved to a different position are not changes. The files are not uploaded.

This guide covers why a line diff is the wrong tool for tables, how the key is chosen, how to read the diff CSV, and what the comparison deliberately leaves alone.

## Why a Line Diff Fails on Tables

A text diff such as [Diff Checker](/text/diff-checker/) compares files line by line, in order. That is right for code and wrong for most exports:

- **Sorting changes everything.** Export the same table sorted by name instead of by date, and every line moves. A line diff reports the whole file; a key comparison reports nothing.
- **One changed cell is a whole changed line.** A line diff shows that row 214 differs. It does not say that the difference is the `price` column, from `3.00` to `3.50`.
- **Added and removed rows interleave.** Without a key, a new row next to a deleted one reads like an edit.

A spreadsheet `VLOOKUP` can match rows by key, but it looks up one column in one direction per formula. Finding rows that exist in only one file, in both directions, and every column that changed takes several formulas and helper columns.

## How to Compare Two Exports

### Step 1: Add both files

Open [CSV Diff](/convert/csv-diff/) and drop the older export into File A and the newer one into File B, or paste the text. Comma, semicolon, tab and pipe are detected per file from its first line, so a TSV can be compared with a CSV.

### Step 2: Check the key

The tool suggests a key column the two files share. Keep it, pick a different column, or pick several columns to form a composite key. You can also compare whole rows instead of matching by key.

### Step 3: Set how keys are matched

Trim, on by default, ignores spaces around key values. Ignore case, off by default, matches `ADA@example.com` with `ada@example.com`. Both affect only how rows are paired, never whether a cell changed.

### Step 4: Read and download the result

The summary counts added, removed, changed and unchanged rows. Changed rows show the old and new value of each changed cell. Download the diff CSV to keep the result or open it in a spreadsheet.

## Which Column Becomes the Key?

When you pick no key, CSV Diff chooses one in this order:

| Order | Rule | Example |
|---|---|---|
| 1 | A shared column named `id`, `email`, `sku`, `uuid`, `key` or `code` (any letter case, spaces around the name ignored) | `ID`, `Email` |
| 2 | Otherwise, the first shared column whose values are unique in both files | `order_number` |
| 3 | Otherwise, no key: whole rows are compared, and a note says so | |

A composite key matches a row only when every key column matches. With `email` + `country` as the key, `ada@x.io` in the UK and `ada@x.io` in the US are two different rows.

## A Worked Example

File A:

```csv
id,name,price
1,Apple,1.00
2,Banana,0.50
3,Cherry,3.00
```

File B:

```csv
id,name,price
1,Apple,1.00
3,Cherry,3.50
4,Date,2.00
```

With no key picked, CSV Diff matches on `id` and reports 1 added, 1 removed, 1 changed and 1 unchanged row. Row 2 was removed, row 4 was added, and row 3 changed in the `price` column only. The diff CSV is:

```csv
change_type,id,name,price,old_price
removed,2,Banana,0.50,
changed,3,Cherry,3.50,3.00
added,4,Date,2.00,
```

Sort File B by name and compare again: the counts do not change, because rows are matched by key, not by position.

## How to Read the Diff CSV

The diff CSV is comma-separated and has one row per added, removed or changed row. Unchanged rows are left out.

| Column | What it holds |
|---|---|
| `change_type` | `added`, `removed` or `changed` |
| Every column of File A, then the new columns of File B | Removed rows hold File A's values; added and changed rows hold File B's |
| `old_<column>` | File A's value, filled on changed rows only, for each column that changed in at least one row |

Removed rows come first, then changed rows, then added rows. Through the API, the list of rows in the response stops at `max_rows` (5,000 by default), but the counts and the diff CSV always cover every row.

## What Counts as a Change?

A row is changed when a column present in both files holds a different value. Cells are compared exactly as written:

- `1.00` and `1.0` are different values. The tool compares text, not numbers.
- A trailing space in a cell is a change, even with Trim on. Trim applies to keys only.
- A column that exists in only one file is not compared, and a note names it.

Two more rules keep the result honest. If a key appears twice in one file, the first row with that key is compared and the repeats are listed as duplicate keys. If a quoted field never closes, the comparison stops and names the file and line instead of comparing a broken table.

## Key Mode or Whole-Row Mode?

| Question | Mode | What you get |
|---|---|---|
| Which records were added, removed or edited? | Key | Added, removed and changed, with cell-level changes |
| Which exact rows are new or missing, with no reliable ID? | Whole row | Added and removed only; a changed row appears as one removed and one added |
| Did anything change at all? | Either | Zero added, removed and changed means the tables hold the same data |

Whole-row mode counts repeats: if File A has a row twice and File B once, one copy is reported as removed.

## Common Mistakes

**Putting the newer file in File A.** The labels flip: new rows appear as removed. Keep the older export in A.

**Keying on a column that is not unique.** A name or a date repeats, so later rows with the same value are only listed as duplicate keys and never compared. Use an ID, or build a composite key.

**Opening the exports in a spreadsheet first.** Spreadsheet apps can drop leading zeros or turn values into dates. Compare the exported text, then open the diff CSV.

**Expecting duplicate rows to be removed.** CSV Diff compares; it does not clean. Remove repeats first with [CSV Dedupe](/convert/csv-dedupe/).

## Use It From Code

The same comparison is available through the FindUtils REST API and as the `csv_diff` tool on the MCP server. Text sent there is processed on the server rather than in your browser.

```bash
curl -X POST https://api.findutils.com/api/tools/csv-diff/execute \
  -H "Content-Type: application/json" \
  -d '{"a":"id,name,price\n1,Apple,1.00\n2,Banana,0.50\n","b":"id,name,price\n1,Apple,1.20\n3,Cherry,3.00\n","keys":["id"]}'
```

Other arguments: `mode` (`key` or `row`), `trim`, `ignore_case`, `delimiter` (`auto`, `,`, `\t`, `;`, `|`), `header` and `max_rows`. The two files together can be up to about 10 MB. See the [API reference](/api/csv-diff/) and the [MCP reference](/mcp/csv-diff/).

## Tools Used in This Guide

| Tool | Use |
|---|---|
| [CSV Diff](/convert/csv-diff/) | Compare two CSV or TSV files by key: added, removed and changed rows |
| [CSV Dedupe](/convert/csv-dedupe/) | Remove duplicate rows before comparing |
| [CSV Merger](/convert/csv-merger/) | Combine several CSV files into one |
| [CSV Viewer](/convert/csv-viewer/) | Read a large CSV as a table |
| [Diff Checker](/text/diff-checker/) | Line-by-line text diff, for files that are not tables |
| [JSON Diff](/developers/json-diff/) | Compare two JSON documents by value |

## FAQ

### How do I compare two CSV files and find the differences?

Put the older file in File A and the newer one in File B of CSV Diff, and match the rows by a key column such as `id` or `email`. The result lists rows only in B as added, rows only in A as removed, and matched rows with a different cell as changed.

### Does the order of the rows matter?

No. Rows are matched by key, so sorting either file differently changes nothing in the result.

### Can I compare two Excel files?

Export each sheet as CSV first, then compare the CSV files. CSV Diff reads delimited text, not `.xlsx` workbooks.

### Why does a row show as changed when the values look the same?

The text differs: a trailing space, `1.0` against `1.00`, or a different letter case. Cells are compared exactly as written; Trim and Ignore case apply only to matching keys.

### Are my files uploaded?

On the page, no. Both files are read and compared in your browser tab.

## Next Steps

Compare your two exports with [CSV Diff](/convert/csv-diff/). If either file has repeated records, clean it first with [CSV Dedupe](/convert/csv-dedupe/).
