Toolvore

JSON Diff

Compare two JSON documents and highlight what changed.

This tool runs entirely in your browser. Your data is never uploaded, never stored, and never leaves your device.

Two JSON documents that differ only in key order or indentation are identical as far as this tool is concerned; what it reports is the structural delta — one line per path, additions marked +, removals −, and changed values rendered as from → to.

How to use it

  1. 1Put the before state in box A and the after state in box B; both must parse, and a failure tells you which side broke.
  2. 2Press Compare — nothing runs until you ask, so a half-typed document does not spam you with errors.
  3. 3Work down the path list: dotted segments are object keys, bracketed ones are array indices.

Example

Input
A: {"a":1,"b":2} B: {"b":2,"a":9,"c":3}
Output
a: 1 → 9 · + c: 3

Key b moved but did not change, so it is not reported. Reordering keys is invisible here, which is the whole point of comparing structure rather than text.

What happens to your data

This tool runs entirely in your browser. Your input is never uploaded to a server, never stored, and never logged. Both documents are parsed with the browser's native JSON engine and compared in memory; there is no history, so navigating away discards both sides.

Last updated August 2026

A deploy goes out, one endpoint starts returning something the client cannot read, and you have the old response saved in a ticket and the new one in front of you. Both run to a few hundred lines. Read them side by side and you will find the field that changed on the third pass, if you find it at all.

Before you paste anything, decide which of two comparisons you actually want. A text diff answers a question about the file — which lines someone edited, where the indentation moved, what a commit contains — and your version control already does that job well. A structural comparison answers a question about the data: which values differ once both sides have been parsed and the formatting has stopped mattering. If you are reviewing a change to a checked-in config, the first one is what you need. If you are asking why one payload behaves differently from another, the second is the only one that will not bury the answer under reformatting noise.

What matters most is what you believe about arrays. Objects in JSON have no defined order — RFC 8259 calls an object an unordered collection of name and value pairs — but arrays do, and position is the only identity an element has. Two lists holding the same records in a different sequence are structurally different at nearly every index, and that is the most common surprise people hit. Where order carries no meaning in your data, a set of tags or a list of permissions, sort both sides on a stable key before comparing, or you will get a report about everything rather than about the one thing that changed.

How it works

Toolvore runs JSON.parse on each side — A first, so a broken A hides anything wrong with B — and then walks both results in step. Values that are strictly equal end that branch of the walk immediately. Two objects are compared on the union of their keys, so a key present on only one side is an addition or a removal, and the sequence the keys were written in never enters into it. Two arrays are compared index against index, which is where the method is weakest: nothing matches elements across the two sides, so an element inserted near the front reports every later position as changed, and the surplus indices of the longer array come back as additions or removals. Reported values are printed with JSON.stringify, so an added object arrives in full on one line however large it is. Nothing survives from the original text — whitespace, number formatting and duplicate keys are all resolved at parse time, which is why 1.0 and 1 are one value. Editing either side leaves the previous result on screen until you press Compare again.

Common use cases

  • Finding which field changed between two API responses after a deploy
  • Comparing a config that works against one that has stopped loading
  • Checking what a migration or script actually altered in an exported record
  • Lining up a staging settings blob against the production one
  • Confirming a refactor left a serialised payload byte-for-byte equivalent
  • Spotting a renamed or dropped key after a third-party API version bump
  • Checking a test fixture against the real response when an assertion fails

Frequently asked questions

How do I compare two JSON files and see exactly what changed?

Open both, copy the contents out and put the older state on the left. What comes back is a list of paths rather than a rewritten document, and that is the part people find unfamiliar at first. A path such as items[3].price.currency is the route from the root down to the value that moved: dotted segments are object keys, bracketed numbers are array positions. Read it as directions rather than as a line number. Nothing here opens files from disk, so a large file means copying its text; and a structural comparison will never mention reindentation, which is exactly why the list is short enough to read.

Does the order of keys in a JSON object matter?

Not to the format. RFC 8259 describes an object as an unordered collection of name and value pairs, so no consumer is entitled to depend on the sequence, even though several parsers happen to preserve the order they read. Arrays are the opposite case: their order is part of the data and changing it changes the document. The one place key order does bite is signing and hashing. A signature computed over serialised text breaks the moment a re-serialisation reorders keys, which is why canonicalisation schemes exist for that job. For everything else, treat a reordering as a non-event and write code that never assumes it.

Why does my comparison mark every array item as changed when I only inserted one?

Because arrays are compared position against position — index 0 against index 0, index 1 against index 1 — and inserting at the front shifts everything after it by one, so each pair now holds two different records. There is no notion of a moved element and no key by which records could be matched across the two lists. Two practical ways round it: sort both sides by id before you compare, so equal records land at equal positions, or restructure the data into an object keyed by id, where a comparison is by key and insertions cost one line. The second is worth doing anyway for anything you compare regularly.

Is it safe to paste a production API response containing customer data?

Here, yes: parsing and comparison both happen inside the page, there is no upload and no request carrying either document, and nothing is stored, so closing the tab ends it. That is worth caring about, because the payloads people compare are precisely the ones holding names, addresses, order records and tokens. The general habit matters more than any single site: a comparison that posts to a server puts your payload into someone else's logs, where retention is their policy rather than yours. If a response carries a bearer token or an API key, treat pasting it anywhere at all as handing over a live credential.

Why do two numbers that look different come out as equal?

JSON numbers become JavaScript doubles before anything is compared, so 1, 1.0 and 1e0 are one value and a change in how a number was written is invisible. Usually that is what you want. The cost sits at the top of the range: integers past 2 to the 53rd, roughly nine quadrillion, cannot be held exactly, so two long identifiers differing only in their final digits can round to the same double and be reported as identical. Anything that is an id rather than a quantity — snowflake ids, account numbers, high-precision decimals for money — belongs in the JSON as a string, for this reason and for several others downstream.

What makes JSON invalid, and why is my snippet rejected?

Strict JSON is much narrower than a JavaScript object literal, and the rejections cluster around a few habits. No trailing comma after the last element or property. Keys and strings need double quotes; single quotes and bare keys are not legal. No comments of either kind. NaN, Infinity and undefined are not values. A common one is not a syntax problem at all: NDJSON, one object per line with no wrapping array, is a stream of documents rather than one document. When a side fails to parse, the message names which side broke and repeats the browser's own wording, which normally includes the character position to look at.

What is the difference between a key set to null and a key that is missing?

They are distinct states and are reported differently: a key present on one side and absent on the other is an addition or a removal, while a key on both sides with null on one is a changed value. The distinction is not pedantry. Many APIs read a field set to null in a PATCH body as an instruction to clear that field, and read its absence as leave this alone, so the two produce opposite outcomes. Languages disagree too — some collapse absent and null into the same thing on the way in, which is how a field quietly disappears between a client and the record it was meant to update.

How do I ignore fields like timestamps, ids or version numbers when comparing?

There is no exclusion list here, so the practical route is to strip the noisy fields before you paste. A jq expression with del on both sides does it in one line, as does a short loop in a console. It is worth the trouble, because an updatedAt, a requestId and an ETag change on every single call and will bury the field you were looking for. The same preparation solves ordering: pipe both sides through a sort on a stable key first. If you are doing this after every test run, the comparison belongs in the test suite with its exclusions written down, not in a manual paste.