← All Posts

How to Compare Two JSON Objects Online (JSON Diff)

Published on February 10, 2026

JSON (JavaScript Object Notation) is the backbone of modern web APIs, configuration files, and data exchange. When you are debugging an API response that suddenly changed, tracking configuration drift across environments, or reviewing data migrations, you need a reliable way to compare two JSON objects and pinpoint exactly what differs between them.

In this guide, you will learn everything about JSON diffing: why it matters, the difference between shallow and deep comparison, how to read diff output, and practical examples you can apply right away.

Why Compare JSON Objects?

Comparing JSON objects is a task developers encounter more often than they might expect. Here are the most common scenarios where a JSON diff tool becomes indispensable.

1. API Response Debugging

When an API endpoint starts returning unexpected data, the fastest way to diagnose the problem is to compare the current response against a known-good response. A JSON diff immediately highlights which fields changed, which were added, and which disappeared. This is far more reliable than scanning two large JSON blobs by eye.

For example, suppose your frontend breaks after a backend deployment. You saved the previous API response and can now compare it against the new one:

// Previous response (known good)
{
  "user": {
    "id": 42,
    "name": "Alice",
    "email": "alice@example.com",
    "role": "admin"
  }
}

// Current response (broken)
{
  "user": {
    "id": 42,
    "name": "Alice",
    "email": "alice@example.com",
    "roles": ["admin"]
  }
}

A diff tool would show that the role field (a string) was removed and a new roles field (an array) was added. That single insight tells you the backend team changed the data model from a single role to multi-role support, and your frontend needs to be updated.

2. Configuration Drift Detection

Modern applications use JSON for configuration: package.json, tsconfig.json, deployment manifests, feature flags, and more. When you maintain multiple environments (development, staging, production), configurations inevitably drift apart. Comparing the JSON config files across environments helps you catch missing keys, different values, or unintended overrides before they cause production incidents.

3. Data Migration Validation

After running a data migration script, you want to verify that the output matches your expectations. Comparing the transformed JSON against a hand-crafted expected output ensures no fields were lost, no types were changed inadvertently, and all transformations were applied correctly.

4. Test Snapshot Comparison

Many testing frameworks use JSON snapshots to capture component output or function return values. When a snapshot test fails, the diff between the old and new snapshot tells you exactly what changed, helping you decide whether to update the snapshot or fix a regression.

Shallow vs Deep Comparison

Not all JSON comparisons are created equal. Understanding the difference between shallow and deep comparison is essential for picking the right approach.

Shallow Comparison

A shallow comparison only checks the top-level keys and their immediate values. It does not recurse into nested objects or arrays. This is fast but misses differences buried inside nested structures.

// Shallow comparison example in JavaScript
function shallowEqual(obj1, obj2) {
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);

  if (keys1.length !== keys2.length) return false;

  for (const key of keys1) {
    // Only checks reference equality for nested objects
    if (obj1[key] !== obj2[key]) return false;
  }
  return true;
}

const a = { name: "Alice", settings: { theme: "dark" } };
const b = { name: "Alice", settings: { theme: "light" } };

shallowEqual(a, b); // false — but only because
                     // settings objects have different references
                     // It does NOT tell you that "theme" changed

Deep Comparison

A deep comparison recursively traverses every level of both JSON structures, comparing each primitive value, each array element, and each nested object. This is the approach used by professional JSON diff tools because it produces a complete, precise report of every difference.

// Deep comparison (simplified recursive approach)
function deepDiff(obj1, obj2, path = "") {
  const diffs = [];
  const allKeys = new Set([
    ...Object.keys(obj1 || {}),
    ...Object.keys(obj2 || {}),
  ]);

  for (const key of allKeys) {
    const currentPath = path ? path + "." + key : key;
    const val1 = obj1?.[key];
    const val2 = obj2?.[key];

    if (val1 === undefined) {
      diffs.push({ type: "added", path: currentPath, value: val2 });
    } else if (val2 === undefined) {
      diffs.push({ type: "removed", path: currentPath, value: val1 });
    } else if (typeof val1 === "object" && typeof val2 === "object"
               && val1 !== null && val2 !== null) {
      diffs.push(...deepDiff(val1, val2, currentPath));
    } else if (val1 !== val2) {
      diffs.push({
        type: "changed",
        path: currentPath,
        oldValue: val1,
        newValue: val2,
      });
    }
  }
  return diffs;
}

Understanding Diff Results: Additions, Removals, and Changes

When you compare two JSON objects, the diff output categorizes every difference into one of three types.

Additions

A key or value exists in the second (right-side) JSON but not in the first (left-side). This typically appears in green in visual diff tools. Additions occur when new fields are introduced in an API, new config options are added, or new array elements appear.

// Left (original)
{
  "database": {
    "host": "localhost",
    "port": 5432
  }
}

// Right (updated) — "ssl" was added
{
  "database": {
    "host": "localhost",
    "port": 5432,
    "ssl": true
  }
}

// Diff output:
// + database.ssl: true

Removals

A key or value exists in the first JSON but not in the second. Shown in red in visual tools. Removals are often the most dangerous type of change because consuming code may depend on the removed field and break at runtime.

// Diff output:
// - database.password: "s3cret"
// This removal could break any code that reads database.password

Changes (Modifications)

A key exists in both objects but its value differs. This includes type changes (string to number), value changes (one string to another), and structural changes (object to array).

// Diff output:
// ~ database.port: 5432 → 3306        (value changed)
// ~ user.role: "admin" → ["admin"]     (type changed: string → array)

Handling Arrays in JSON Diffs

Arrays are the trickiest part of JSON comparison. Unlike objects where keys provide a natural alignment, array elements are identified by their index. If an element is inserted at the beginning of an array, every subsequent element shifts, making a naive index-based comparison report that every element changed.

// Left
{ "tags": ["frontend", "react", "typescript"] }

// Right — "javascript" was inserted at the start
{ "tags": ["javascript", "frontend", "react", "typescript"] }

// Naive index-based diff (misleading):
// ~ tags[0]: "frontend" → "javascript"
// ~ tags[1]: "react" → "frontend"
// ~ tags[2]: "typescript" → "react"
// + tags[3]: "typescript"

// Smart diff (what actually happened):
// + tags[0]: "javascript" (inserted at beginning)

Advanced diff algorithms use techniques like the Longest Common Subsequence (LCS) to detect insertions and deletions within arrays rather than treating each index as an independent comparison. PulpMiner's JSON Diff tool uses smart array comparison so you see meaningful results.

Practical Example: Comparing API Responses

Let us walk through a real-world scenario. You are integrating with a third-party payment API that recently released version 2. You need to understand what changed between v1 and v2 responses.

// Payment API v1 response
{
  "transaction_id": "txn_abc123",
  "amount": 29.99,
  "currency": "USD",
  "status": "completed",
  "customer": {
    "id": "cust_001",
    "email": "buyer@example.com"
  },
  "created_at": "2026-01-15T10:30:00Z"
}

// Payment API v2 response
{
  "id": "txn_abc123",
  "amount": {
    "value": 2999,
    "currency": "USD"
  },
  "status": "succeeded",
  "customer": {
    "id": "cust_001",
    "email": "buyer@example.com",
    "name": "Jane Doe"
  },
  "created_at": "2026-01-15T10:30:00Z",
  "metadata": {}
}

Running this through a JSON diff reveals several breaking changes: transaction_id was renamed to id, the amount field changed from a number to an object (with the value now in cents), status values changed from "completed" to "succeeded", the currency field moved inside the amount object, and new fields (customer.name, metadata) were added. Each of these differences requires a code change in your integration.

Comparing JSON Programmatically

If you need to compare JSON in code (for tests, CI pipelines, or automation), here are approaches in popular languages.

JavaScript / TypeScript

// Using the deep-diff library
import { diff } from "deep-diff";

const left = { name: "Alice", age: 30, hobbies: ["reading"] };
const right = { name: "Alice", age: 31, hobbies: ["reading", "hiking"] };

const differences = diff(left, right);
// [
//   { kind: "E", path: ["age"], lhs: 30, rhs: 31 },
//   { kind: "A", path: ["hobbies"], index: 1,
//     item: { kind: "N", rhs: "hiking" } }
// ]
// E = edited, A = array change, N = new element

Python

# Using the deepdiff library
from deepdiff import DeepDiff
import json

left = {"name": "Alice", "age": 30, "hobbies": ["reading"]}
right = {"name": "Alice", "age": 31, "hobbies": ["reading", "hiking"]}

result = DeepDiff(left, right)
print(json.dumps(result, indent=2))
# {
#   "values_changed": {
#     "root['age']": { "new_value": 31, "old_value": 30 }
#   },
#   "iterable_item_added": {
#     "root['hobbies'][1]": "hiking"
#   }
# }

Tips for Effective JSON Comparison

  • Format before comparing — Minified JSON is hard to diff. Pretty-print both objects first so the diff tool can align keys properly.
  • Sort keys consistently — JSON object key order is not guaranteed. Sorting keys alphabetically before diffing prevents false positives from key reordering.
  • Ignore volatile fields — Fields like timestamps, request IDs, and random tokens change on every request. Filter these out before comparing to focus on meaningful differences.
  • Use semantic comparison for arrays — If array order does not matter (like a list of tags), consider sorting the array before comparison to avoid noise from reordering.
  • Document expected diffs — When migrating APIs, keep a record of expected differences so your team knows which changes are intentional.

Need to extract data from websites?

PulpMiner turns any webpage into structured JSON data. No scraping code needed.

Try PulpMiner Free

No credit card required