JSON Diff Checker

Compare two JSON documents as values, not text — every added, removed and changed leaf listed at its exact path, with key order and formatting correctly ignored. Free, no sign-up — runs locally in your browser.

Both sides are auto-saved in this browser only, so a refresh won't lose them. Nothing is uploaded.

+ added in the right document − removed from the left document ~ value changed

Why a structural diff, when text diff exists?

Because JSON's meaning is not in its text. Reorder two keys and a text diff lights up while nothing changed; reformat a response with different indentation and every line “changed”. Conversely, the one-character edit that matters — a price going from 59.99 to 59.98, an ID arriving as a string instead of a number — drowns in that noise. This checker parses both documents and compares the values themselves, then reports each difference at the path where it lives: $.items[2].price, old value beside new. It is the tool for the recurring questions of API work — what did this deploy change in the response, why does the staging payload fail where production passes, did the migration actually preserve the data — where the answer needs to be a list of facts, not a wall of red and green.

How to use this tool

  1. Paste the original into the left box and the changed document into the right. Either side may be a single value — an array of records diffs as readily as an object.
  2. Press Compare. The summary counts additions, removals and changes; the list below shows each at its full path.
  3. Read + rows as “only in the right document”, − rows as “only in the left”, and ~ rows as “both, with different values” — the +/− signs and the words carry the meaning, so the list survives any colour blindness or monochrome printout.
  4. Swap sides flips the framing when you realise you pasted them backwards — additions become removals, as they should.
  5. Copy or download the report as text for a ticket, a pull request or a changelog.

The comparison rules

Objects match by key, insensitive to key order; a key whose value became null is a change, a key that disappeared is a removal — the difference matters and the report keeps it. Arrays match by index, and the tool states plainly what follows: an insertion at the front reports the downstream shifts as changes. Type changes (number to string, object to array) are single change entries at the path, never a cascade. Whole subtrees that appear or disappear collapse to one entry with a preview. Numbers are compared as numbers — 1 and 1.0 are one value in JSON, and the diff treats them so. The report caps at 500 entries and says so when it truncates.

Worked example

Left is yesterday's response, right is today's (note the reordered keys):

left:  { "id": 7, "price": 59.99, "stock": 12 }
right: { "stock": 12, "price": 61.5, "id": 7, "currency": "USD" }

The structural diff reports exactly two facts:

~ $.price — left: 59.99 → right: 61.5
+ $.currency — right: "USD"

— and stays silent about the key order, because nothing about it changed.

Frequently asked questions

How are the two documents compared — textually or structurally?

Structurally. Both sides are parsed into values first, then walked side by side: object keys are matched by name regardless of order (so reordering keys is not a change), nesting is followed into sub-objects and arrays, and each difference is reported at its path — $.user.address.city, $.items[2].price — with the old and new values side by side. Two documents that differ only in whitespace and key order produce an empty diff, which a text comparison could never tell you.

How are arrays compared?

By index: element 0 against element 0, element 1 against element 1, with extra elements on either side reported as added or removed at their index. That is the only honest general rule — arrays are ordered in JSON, and position is part of their meaning. Be aware of what it implies: inserting one element at the front of a list reports every following element as changed, because at each index the values really are different. For id-keyed collections, that avalanche is the signal to sort or key the data before diffing, not a defect in the diff.

Is the number 1 different from 1.0, or from "1"?

1 and 1.0 are the same JSON number and are not reported — JSON itself draws no distinction, and neither do parsers. 1 and "1" are different types (number versus string) and are always reported as a change; that confusion is one of the most common real-world API bugs, and catching it is half the reason this tool exists. The same strictness applies to true versus "true", and null versus a missing key — which are reported differently: a key set to null is a change, a key that vanished is a removal.

What happens with very large diffs?

The report is capped at 500 differences so the page stays responsive and readable. If your comparison hits the cap, the summary says the list was truncated — and a diff that large is telling you something in itself: the two documents differ in kind, not in detail (a schema migration, a wrong fixture, an environment mix-up). Diff generation-shaped samples instead: one representative record from each side usually isolates the structural change you actually care about.

Why does an added object show as a single line, not every field inside it?

Because reporting each nested field of a wholly new subtree would bury the headline — "this block appeared" — under dozens of rows you would have to mentally reassemble. Additions and removals of entire objects or arrays collapse to one entry at the subtree's path, with a compact preview of the value. Changes inside something present on both sides are always expanded to the precise leaf path, which is where line-by-line detail earns its place.

Related tools