How to Compare Two JSON Files Online
Comparing two JSON documents by eye is slow and unreliable: a single changed value can hide inside thousands of lines of nested objects and arrays. Paste the original on the left and the modified version on the right, and this tool lists every difference with its exact path — so you can review API changes, config updates and test fixtures in seconds instead of scrolling through walls of text.
The comparison is semantic rather than textual. Both documents are parsed into data structures first, so a reordered key or a different indentation never counts as a difference: {"name":"Alice"} and a pretty-printed version of the same object compare as identical. Only genuine changes in values, types or structure are reported.
It also runs entirely in your browser. JSON payloads routinely contain session tokens, customer records and internal endpoints, and none of it is uploaded here: parsing, comparison and rendering all happen on your device, and closing the tab leaves nothing behind.
Paste or drop both documents
Drop a .json file into either panel, paste the raw text, or press Load example to see the whole workflow with realistic data. Each panel has Format and Clear buttons so you can pretty-print one side before comparing without leaving the page. Files never leave the browser, so there is no upload step and no account.
The example document is a small order payload with a nested user object and an items array. Loading it produces four differences immediately, which makes it a good way to see how paths and value types are reported before you bring your own data.
What the diff reports
Every difference is classified into one of three kinds, and each carries the full path to the value that changed.
| Kind | Meaning | Example |
|---|---|---|
| Added | The right document has a value the left one lacks | user.email appears only on the right |
| Removed | The left document has something the right one dropped | user.phone exists only on the left |
| Changed | Both sides have the same path, but the value or type differs | user.name goes from "Alice" to "Bob" |
The example above produces exactly this output:
| Path | Change | Before | After |
|---|---|---|---|
user.name | Changed | "Alice" | "Bob" |
user.phone | Removed | "123456789" | — |
user.email | Added | — | "alice@example.com" |
items[1].name | Changed | "B" | "C" |
A summary line counts the totals — for instance "4 differences found · 1 added · 1 removed · 2 changed" — and the filter buttons narrow the list to a single kind. Every path has a Copy Path button, so you can paste items[1].name straight into your editor, a test assertion or a ticket.
Semantic JSON diff vs text diff
A line-based diff compares characters, so it reports changes that are not changes at all:
| Change | A text diff shows | A semantic JSON diff shows |
|---|---|---|
{"a":1,"b":2} reordered to {"b":2,"a":1} | Two changed lines | No difference |
| Two-space indent changed to four spaces | Every line changed | No difference |
Number written as 1.0 instead of 1 | Changed | No difference — same value |
Number 1 vs string "1" | Changed | Changed — different type |
Array [1,2,3] vs [3,2,1] | Changed | Changed — order is meaningful |
Boolean true vs string "true" | Changed | Changed — different type |
The rest of this section explains why each row lands where it does.
- Key order —
{"a":1,"b":2}and{"b":2,"a":1}describe the same object. A text diff flags both lines; a semantic diff reports nothing. - Whitespace and indentation — reformatting a payload from two spaces to four produces a large text diff and an empty semantic one.
- Number formatting —
1and1.0are the same JSON number value, so they compare as equal. - Type changes are real — the number
1and the string"1"are genuinely different, and so aretrueand"true". Those are reported as changed, because a consumer parsing the payload would behave differently.
Nested objects are compared at any depth and reported under their full path, such as user.profile.name or settings.notifications.email. Keys that are not valid identifiers use bracket notation, so {"content-type":"json"} becomes ["content-type"].
Arrays are compared by index, because element order usually carries meaning in JSON: [1,2,3] and [3,2,1] are different documents. A changed element shows up as items[1].name, and extra elements on either side appear as added or removed at their index.
Comparing API responses
The most common real-world use is checking what an endpoint actually changed. Fetch the response before and after a deploy, save each as a .json file, and drop them into the two panels. Volatile fields such as timestamps, request IDs and pagination cursors will dominate the diff unless you remove them first, which is easy with jq:
# strip volatile fields, then sort keys for a stable comparison
jq 'del(.meta.requestId, .meta.timestamp) | .items |= sort_by(.id)' before.json > a.json
jq 'del(.meta.requestId, .meta.timestamp) | .items |= sort_by(.id)' after.json > b.json
Drop a.json and b.json into the tool and every remaining difference is a real contract change. Because the comparison happens locally, this is also safe for staging payloads that contain real customer data.
The same workflow works for config files: compare a working config.json against one from a failing environment, and the diff usually points at the changed connection string or feature flag immediately.
Ignoring fields you do not care about
This tool compares whole documents and deliberately does not hide paths for you — a diff that silently ignores fields is how real regressions get missed. If a payload contains fields you want to exclude, remove them first and compare the cleaned copies:
jq 'del(.updatedAt, .traceId)' payload.json > payload-clean.json
That keeps the comparison honest: what you see in the results is exactly what differs between the files you supplied.
Compare JSON in code
If you need this inside a test suite or a CI job rather than in a browser, the same semantics are a few lines away.
// Node: order-insensitive deep equality, no dependency required
import assert from "node:assert/strict";
assert.deepStrictEqual(JSON.parse(before), JSON.parse(after));
Python users can reach for json plus a recursive walk, or deepdiff when readable output matters more than zero dependencies. For shell scripts, jq --sort-keys normalises key order before a plain diff:
diff <(jq -S . before.json) <(jq -S . after.json)
The browser tool is faster for one-off reviews and for payloads you would rather not paste into a terminal history; the code snippet is better once the comparison becomes a permanent check.
If either document is hard to read because it arrived minified, the JSON Formatter & Validator can beautify and validate it first — both tools share the same parser, so a document that formats cleanly will also compare cleanly. Tokens and encoded values inside an API response can be inspected with the JWT Decoder or the Base64 Encoder & Decoder.
How to Use This Tool
- Paste the original JSON into the Left panel — or drop a
.jsonfile onto it, then use Format or Clear as needed. - Paste the modified JSON into the Right panel the same way.
- Click "Compare JSON" to parse both documents and compute their semantic differences.
- Review the summary — total count plus the added / removed / changed breakdown — and filter the list by kind.
- Copy any path with one click to jump straight to it in your editor.
Malformed input never produces a misleading diff: if either side fails to parse, the tool reports which side is invalid along with the exact line and column, and the comparison waits until both documents are valid.
Limits and troubleshooting
- Size — each side accepts up to 5 MB of text (5,000,000 characters). The interface warns you past 500,000 characters, because very large documents take noticeably longer to parse and diff. If you hit the limit, split the payload or extract the relevant subtree with
jq. - Invalid JSON — trailing commas, single quotes and comments are not JSON. The error message points at the line and column so you can fix it, and the JSON Formatter & Validator reports the same diagnostics.
- Non-finite numbers — values such as
1e999, which parse toInfinity, are rejected rather than silently treated as equal. - Empty panels — both sides must contain a document; the tool tells you which panel is missing input instead of guessing.
- Array order — if your diff is full of array changes, sort the arrays first (as in the
jqexample above) so only real changes remain.
Benefits
- Semantic, not textual — key order and whitespace never count as differences.
- Precise paths — every change is located, from
user.nametoitems[2].id. - Added / Removed / Changed — differences are classified so reviews stay fast.
- 100% private — parsing and comparison happen locally; nothing is uploaded.
- File-friendly — drop a
.jsonfile into either panel, no signup needed. - Free forever — no account, no limits beyond browser memory and no watermarks.
