Skip to content
DevPenh

Compare Staging and Production API Responses

A repeatable response diff: strip volatile fields with jq, compare JSON by path, and separate real API changes from timestamps and array order.

By Yorn Chanpenh · Last reviewed

Staging and production can return different JSON for the same request even when the code is identical: timestamps, request IDs, array order and number formatting all change. A text diff then reports noise and hides the real difference. This workflow strips the noise first, then compares by JSON path.

The jq and diff commands were checked with jq 1.7.1. The JSON examples are intentionally small.

1. Capture both responses

Keep the raw responses. Do not compare a copied browser preview with a terminal output; whitespace and truncation differ.

curl -sS -H "Authorization: Bearer $STAGING_TOKEN" \
  "$STAGING_URL/api/orders/42" > staging.json

curl -sS -H "Authorization: Bearer $PROD_TOKEN" \
  "$PRODUCTION_URL/api/orders/42" > production.json

Check the status code and Content-Type before comparing. If one side returned an HTML error page, a JSON diff is meaningless; see how to debug API responses.

2. Remove fields that always change

Sort keys and delete request-specific fields at every depth with jq:

jq -S 'walk(if type == "object" then del(.requestId, .updatedAt) else . end)' \
  staging.json > staging.norm.json

jq -S 'walk(if type == "object" then del(.requestId, .updatedAt) else . end)' \
  production.json > production.norm.json

Replace the field names with the ones your API actually changes on every request. Common examples are requestId, traceId, serverTime and etag. Do not remove a field just because it differs; keep anything that could be the bug.

3. A text diff finds noise, not structure

For these two normalized responses:

{
  "data": [
    { "id": 1, "name": "Desk lamp", "price": 24.5, "stock": 3 },
    { "id": 2, "name": "USB-C cable", "price": 5, "stock": 0 }
  ]
}

{
  "data": [
    { "id": 2, "name": "USB-C cable", "price": 5.0, "stock": 0 },
    { "id": 1, "name": "Desk lamp", "price": 24.5, "stock": 4 }
  ]
}

diff -u staging.norm.json production.norm.json exits with status 1 and marks the whole array because the order changed. The real change — data[?(@.id==1)].stock from 3 to 4 — is buried in the reordering.

4. Compare by path with the JSON Diff tool

Paste the normalized responses into the JSON Diff tool, then:

  1. Choose Arrays: Match by key and enter id for arrays of objects. This pairs the two id: 1 items and shows the field-level change.
  2. Choose Ignore order for arrays that have no stable key.
  3. Add request-specific names to Ignore keys if you did not remove them with jq.

The report lists paths such as $.data[?(@.id==1)].stock with the old and new values. It also distinguishes a value change (3 → 4) from a type change (5 → "5"). See how to compare two JSON files for the comparison rules.

5. Interpret the result

ReportLikely cause
Added or removed keyDifferent API version, feature flag or database row. Check the deploy history and migrations.
Changed valueConfiguration, seed data, user data or a real logic difference.
Type changedSerializer or schema change, such as a number becoming a string. Search the client code for casts.
Only array order changedMissing ORDER BY, or a different query plan. Use Match by key if the API treats the list as a set.

6. Keep a response pair as a regression test

Save the normalized staging and production responses next to the test that exercises the endpoint. When the backend changes, a failing diff is easier to review than a screenshot. If the API returns large responses, keep one fixture per meaningful case rather than one giant snapshot.

Do not paste secrets into a public diff

Remove access tokens, cookies, email addresses and other personal data before sharing a response. The JSON Diff tool and the JSON Formatter run in your browser, but a screenshot or copied report is still a disclosure. Use redacted fixtures for tickets and pull requests.

Part of the API debugging series. Start with How to Debug API Responses: A Practical Workflow.