Comparing data, not characters
Two responses from the same API can be identical in meaning and completely different as text: keys in another order, different indentation, one of them minified. A text diff shows all of that as noise, and buries the one field that actually changed.
This tool parses both sides first and then walks the two values together, reporting each difference with the path that leads to it:
~ user.name "ada" → "grace"
+ user.roles[2] "admin"
- meta.legacyId 4821
! order.total string → number
That’s usually all you need: what changed, and where to look.
What counts as a difference
- Added or removed keys are reported at the path where they appear.
- Changed values show both the old and the new.
- Type changes are called out separately, because
"7"and7behave differently once something consumes them. - Key order is ignored. JSON objects are unordered, so two documents with the same pairs match regardless of how they were written.
- Array order matters by default, because position usually carries meaning. When it doesn’t (a set of tags, a list of permissions), turn on “compare contents, not order” and only genuinely added or removed items are reported.
Missing, null, and empty
Three states that get conflated and shouldn’t be:
| State | Reported as |
|---|---|
| The key isn’t there | added or removed |
The key is there and null |
a type change, if the other side has a value |
The key is there and "" or [] |
a changed value |
If your API distinguishes “not provided” from “explicitly cleared”, this is where you see it.
The side-by-side view
The list of paths is the quickest way to see what changed. For a large payload, or when you want the shape of the whole document, switch to side by side: both documents are pretty-printed with their keys sorted, then compared line by line.
Sorting is what makes that view usable: without it, a reordered key would push everything below it out of alignment and make the whole document look different.
Numbers, precision and long ids
JSON numbers are parsed as standard floating-point values, so an identifier longer than about 16 digits loses precision on both sides before the comparison happens. Two different ids can arrive here looking identical.
It’s the same limitation every JSON parser has, and the fix is the same: send long identifiers as strings.