4 min read

Compare Two JSON API Responses: Key Order, Arrays and Missing Fields

Compare JSON responses from staging and production with a runnable example. Separate formatting from changed values, array order, missing fields and null.

Maintained by Ali Aygör. Report a correction.

Choose a comparison that answers the incident question

Two responses can look different while carrying the same data: one is minified, the other is indented, or object keys appear in a different order. Start by deciding whether you need an exact text comparison or a comparison of parsed JSON values. The distinction matters when reviewing a deployment, a changed webhook or a failing contract test.

For an API incident, capture the same endpoint, method, query parameters and authenticated role in both environments. Keep the status code and Content-Type with each sample. A 401 error body and a successful order response are different outcomes, not necessarily a serialization regression. Redact credentials and customer data before copying either body.

Reproduce a real value change and a formatting-only change

Paste the original response on the left and the updated response on the right in JSON & Text Diff Checker, then select Compare JSON. The action formats both documents and sorts object keys recursively. It leaves array positions significant. Look for changed values after normalization rather than counting raw line differences.

This standalone example shows why comparing serialized text is insufficient. The first two objects contain the same fields and values in a different order. A later change from pending to paid is a real value difference. The function is deliberately limited to JSON-compatible parsed values; it is not a general JavaScript object comparator.

const before = { id: "ord_42", status: "pending", items: [{ sku: "A1", quantity: 1 }] };
const reordered = { items: [{ quantity: 1, sku: "A1" }], status: "pending", id: "ord_42" };
function normalize(value) {
  if (Array.isArray(value)) return value.map(normalize);
  if (value !== null && typeof value === "object") {
    return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, normalize(item)]));
  }
  return value;
}
const equalJson = (a, b) => JSON.stringify(normalize(a)) === JSON.stringify(normalize(b));
console.log(equalJson(before, reordered)); // true
const after = { ...reordered, status: "paid" };
console.log(equalJson(before, after)); // false

Treat arrays, missing fields and null separately

An array is ordered. Swapping the first two search results can be a meaningful ranking change even if both responses contain the same IDs. This tool compares positions; it does not match array objects by an id field. For an unordered business collection, prepare a separate copy sorted by a documented stable key, retain the raw response, and record that choice in the incident notes.

A missing discount field is different from discount: null, and the number 1 is different from the string "1". These distinctions can explain why a client takes a different branch despite apparently similar payloads. A line diff can expose the changed area, but it does not generate JSON Patch operations or prove that a change is backward compatible. Validate the expected shape against your contract next.

Handle timestamps and generated IDs without hiding evidence

Request IDs, generated timestamps and elapsed durations often change on every call. Do not delete every field that differs just to make the comparison green. First identify which fields the contract explicitly allows to vary; then remove only those paths from a copy of each sample. Keep the originals so a timing bug or missing correlation identifier remains recoverable.

For example, two orders with different IDs may be two separate test records rather than a broken response. Prefer a fixed synthetic fixture or a documented test account when reproducing a deployment difference. A useful bug report includes the endpoint, deployment versions, expected invariant, changed field and a minimal redacted sample.

Know when parsing can hide a difference

Compare JSON uses JavaScript JSON.parse and JSON.stringify. Duplicate object keys collapse to the last value; very large numbers and high-precision decimals can be rounded; numeric notation such as 1.0 and 1 normalizes. Keep large identifiers as quoted strings when that is what your API contract specifies. Use the unchanged text mode or a precision-preserving parser when the original number spelling or duplicate keys are evidence.

Parsing is also not decryption. If parsing once returns a string containing JSON, inspect the serialization boundary before parsing another layer. Do not remove backslashes globally: that can change escaped quotes, line breaks and file paths. Validate both complete inputs before drawing a conclusion from a partial or truncated response.

Turn the diff into a regression test

Once you identify the unexpected field, add a focused assertion to a deterministic fixture. Test the intended type and meaning: status must be paid, quantity must be numeric, or discount must be explicitly null. Avoid snapshotting an entire volatile response when only a few contract rules matter.

Repeat the failing request after the fix with the same test data and permissions. Check response status, schema and relevant side effects, not only visual similarity. The browser comparator is a quick inspection step; your automated contract test is what protects the next deployment.

Specifications and references

Continue the investigation

Related tools

Browse all developer tools

Related workflows