Local CSV comparison

What changed in the exported copy?

Choose both files and an exact row-key column. No values or filenames are uploaded or stored. Reload or Clear files removes the current inputs.

Choose local files and matching rules

Required in both files. No guessing, trimming, date conversion, or duplicate-key matching. Header and row order are ignored.

Choose both UTF-8 files, enter the row key, and compare. JavaScript is required for local processing.

Limits per file: 1,048,576 bytes (1 MiB), 10,000 data rows, 100 columns, 200,000 cells. At most 500 detailed differences and 50 ambiguity issues are returned; summaries retain full counts. Use smaller files for complete detail. UTF-8 only; malformed quotes and cells without a header are rejected.

Original synthetic examples · 1.0.0

Try known changes before your own files

Download a pair, choose each file above, enter its listed key, then compare. Expected outcomes were authored independently. These files are fictional, not vendor exports.

Download machine-readable expected results

zeros · Non-key identifiers retain leading zeros; no number conversion.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 1,
    "afterRows": 1,
    "addedRows": 0,
    "removedRows": 0,
    "changedRows": 1,
    "changedValues": 1,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": [
    {
      "type": "changed-value",
      "key": "row1",
      "header": "account",
      "before": {
        "present": true,
        "value": "0012"
      },
      "after": {
        "present": true,
        "value": "12"
      }
    }
  ]
}
dates · Ambiguous dates compared as raw strings without locale inference.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 1,
    "afterRows": 1,
    "addedRows": 0,
    "removedRows": 0,
    "changedRows": 1,
    "changedValues": 1,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": [
    {
      "type": "changed-value",
      "key": "1",
      "header": "date",
      "before": {
        "present": true,
        "value": "03/04/2026"
      },
      "after": {
        "present": true,
        "value": "2026-03-04"
      }
    }
  ]
}
rows · Added and missing rows are reported using the selected exact key.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 2,
    "afterRows": 2,
    "addedRows": 1,
    "removedRows": 1,
    "changedRows": 0,
    "changedValues": 0,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": [
    {
      "type": "removed-row",
      "key": "2",
      "row": [
        {
          "header": "id",
          "value": {
            "present": true,
            "value": "2"
          }
        },
        {
          "header": "note",
          "value": {
            "present": true,
            "value": "gone"
          }
        }
      ]
    },
    {
      "type": "added-row",
      "key": "3",
      "row": [
        {
          "header": "id",
          "value": {
            "present": true,
            "value": "3"
          }
        },
        {
          "header": "note",
          "value": {
            "present": true,
            "value": "new"
          }
        }
      ]
    }
  ]
}
duplicates · Duplicate row keys make the entire comparison unresolved.

Key: id. Delimiters: comma in both files.

Expected outcome: unresolved.

{
  "status": "unresolved",
  "issueCodes": [
    "DUPLICATE_KEY"
  ]
}
empty-missing · An absent trailing cell differs from an explicitly empty cell.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 2,
    "afterRows": 2,
    "addedRows": 0,
    "removedRows": 0,
    "changedRows": 1,
    "changedValues": 1,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": [
    {
      "type": "changed-value",
      "key": "1",
      "header": "note",
      "before": {
        "present": false
      },
      "after": {
        "present": true,
        "value": ""
      }
    }
  ]
}
unchanged-quotes · Quoted delimiters, escaped quotes and embedded newlines remain equal despite row and header reorder.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 2,
    "afterRows": 2,
    "addedRows": 0,
    "removedRows": 0,
    "changedRows": 0,
    "changedValues": 0,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": []
}
headers · Exact header renames are one removed and one added header, with missing-cell differences.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 1,
    "afterRows": 1,
    "addedRows": 0,
    "removedRows": 0,
    "changedRows": 1,
    "changedValues": 2,
    "addedHeaders": 1,
    "removedHeaders": 1
  },
  "differences": [
    {
      "type": "changed-value",
      "key": "1",
      "header": "old",
      "before": {
        "present": true,
        "value": "x"
      },
      "after": {
        "present": false
      }
    },
    {
      "type": "changed-value",
      "key": "1",
      "header": "new",
      "before": {
        "present": false
      },
      "after": {
        "present": true,
        "value": "x"
      }
    }
  ]
}
key-change · Changed key strings cannot be inferred to identify the same row.

Key: id. Delimiters: comma in both files.

Expected outcome: compared.

{
  "status": "compared",
  "summary": {
    "beforeRows": 1,
    "afterRows": 1,
    "addedRows": 1,
    "removedRows": 1,
    "changedRows": 0,
    "changedValues": 0,
    "addedHeaders": 0,
    "removedHeaders": 0
  },
  "differences": [
    {
      "type": "removed-row",
      "key": "0012",
      "row": [
        {
          "header": "id",
          "value": {
            "present": true,
            "value": "0012"
          }
        },
        {
          "header": "note",
          "value": {
            "present": true,
            "value": "x"
          }
        }
      ]
    },
    {
      "type": "added-row",
      "key": "12",
      "row": [
        {
          "header": "id",
          "value": {
            "present": true,
            "value": "12"
          }
        },
        {
          "header": "note",
          "value": {
            "present": true,
            "value": "x"
          }
        }
      ]
    }
  ]
}

Review the CSV import method, then record the result in the acceptance worksheet.

This tool compares raw parsed strings using your explicit row key. Synthetic examples show seeded changes; they are not provider tests or proof of a successful real migration. Our editorial method

Make a small round trip first

Keep an untouched original. Import a small representative copy into the destination, export it again, and choose those two CSV files below. Enter a column name that uniquely identifies each row in both files.

A key such as a stable record ID is usually more useful than a name. If the chosen key itself changes, this tool reports a removed row and an added row; it cannot infer that they represent the same person. Resolve duplicate or missing keys before comparing.

Read differences without guessing repairs

Header names and keys match exactly, including whitespace and case. Row order and header order do not count as changes. Values remain strings: 0012 differs from 12, and 03/04/2026 is not interpreted as a date.

An explicit empty string is different from an absent cell. Missing or duplicate headers and empty, missing, or duplicate keys prevent comparison. A short row is retained as missing cells; an extra cell without a header is rejected.

Choose comma, semicolon, or tab separately for each file. Files must be UTF-8, optionally with a BOM. Quoted delimiters, escaped quotes, and quoted newlines are supported. This is a documented local dialect informed by RFC 4180, not a claim that every CSV dialect is supported.

Turn findings into an acceptance record

Check the seeded examples first. Then examine your own changed values and unexpected row or column changes. Download the JSON difference report if you need a local record; it preserves missing versus empty values and does not rewrite your CSV.

Use the existing CSV acceptance worksheet to record what you verified, unresolved differences, and the person responsible for the final import. A completed comparison is one check, not proof that automations, consent, relationships, or dates behave correctly after migration.

Take it with you

CSV import acceptance worksheet

Download worksheet

Sources & verification

Product details and prices can change. Check the linked provider before buying.

  1. RFC 4180: Common Format and MIME Type for CSV Files (Informational) Accessed 2026-09-14

Sources link directly to providers. Product buttons may use separately labeled affiliate links. Read our disclosure.

Your decision at a glance

Compare the options

Full comparison

Documentation-based guidance. No tested ranking or performance score.

Email

Kit

Consider it for
Evaluate for subscriber broadcasts; consider Creator when a specific sequence or automation earns its cost.
Starting cost
Free plan; audited Creator example $39/month for 1,000 subscribers
Before you choose
Subscriber count and billing interval change price. The paid workflow has not been tested.

Checked 2026-09-13 · Pricing source

View Kit plans

Ordinary provider link. No affiliate partnership claimed. Link disclosure

Automation

Make

Consider it for
A visual tool to investigate small workflow designs, beginning with synthetic data and human review.
Starting cost
Free plan includes 1,000 credits/month
Before you choose
Connected apps can cost extra. Free scheduled interval is at least 15 minutes; paid billing must be checked at checkout.

Checked 2026-09-13 · Pricing source

View Make plans

Affiliate link: we may earn a commission on an eligible purchase. Link disclosure

Email

MailerLite

Consider it for
Compare with Kit for a small list that needs limited automated follow-up.
Starting cost
Free: up to 250 subscribers, 2,500 emails/month, 3 automations
Before you choose
Paid prices require a live quote. Free sends lock above the subscriber limit; premium trial features may expire.

Checked 2026-09-13 · Pricing source

View MailerLite plans

Ordinary provider link. No affiliate partnership claimed. Link disclosure