How to Diff API Responses — Compare REST & GraphQL Output

API responses change. Endpoints get updated, schemas evolve, and bugs sneak in. Comparing API responses is essential for regression testing, debugging, and migration validation.

Why Diff API Responses?

The Challenge: JSON Isn't Simple Text

Naive text diffing of API responses produces noisy results. JSON has properties that make simple line-by-line comparison misleading:

Method 1: Quick Visual Comparison

For one-off checks, paste both responses into a JSON diff tool:

  1. Call both endpoints (e.g., with curl or Postman)
  2. Copy the response bodies
  3. Paste into a JSON diff tool like DiffSnap JSON Diff
  4. Review the highlighted differences

DiffSnap automatically parses and formats JSON, so key ordering and whitespace differences are normalized.

Method 2: Command-Line with jq

For scripted comparisons, jq normalizes JSON before diffing:

# Fetch and normalize both responses
curl -s https://api.example.com/v1/users | jq -S '.' > v1.json
curl -s https://api.example.com/v2/users | jq -S '.' > v2.json

# Diff the normalized output
diff -u v1.json v2.json

The -S flag sorts object keys, eliminating false positives from key reordering.

Ignoring Dynamic Fields

# Remove timestamps and IDs before comparing
curl -s $URL | jq 'del(.timestamp, .requestId, .metadata.generatedAt)' > clean.json

Method 3: Automated Regression Testing

For CI/CD pipelines, automate API response comparison:

#!/bin/bash
# api-regression-test.sh

BASELINE="snapshots/users-baseline.json"
CURRENT=$(curl -s https://api.example.com/users | jq -S 'del(.timestamp)')

echo "$CURRENT" > /tmp/current.json

if ! diff -q "$BASELINE" /tmp/current.json > /dev/null 2>&1; then
  echo "❌ API response changed!"
  diff -u "$BASELINE" /tmp/current.json
  exit 1
fi

echo "✅ API response matches baseline"

Method 4: Snapshot Testing in Code

Testing frameworks like Jest support JSON snapshot testing natively:

// api.test.js
test('GET /users returns expected shape', async () => {
  const res = await fetch('https://api.example.com/users');
  const data = await res.json();
  
  // Remove dynamic fields
  delete data.timestamp;
  delete data.requestId;
  
  expect(data).toMatchSnapshot();
});

On first run, Jest saves the snapshot. On subsequent runs, it diffs against the saved version.

Handling Common Edge Cases

Array Ordering

If array order doesn't matter semantically, sort before comparing:

jq '.users | sort_by(.id)' response.json

Floating Point Precision

APIs may return 0.30000000000000004 vs 0.3. Round before comparing:

jq 'walk(if type == "number" then . * 100 | round / 100 else . end)' response.json

Pagination Differences

When comparing paginated endpoints, fetch all pages first, then concatenate and compare the full dataset.

Best Practices

  1. Normalize before diffing — Sort keys, remove dynamic fields, format consistently
  2. Use semantic diff for JSON — Don't rely on line-by-line text diff
  3. Automate in CI — API regression tests catch breaking changes before deployment
  4. Version your baselines — Store snapshot files in git alongside tests
  5. Document ignored fields — Make it clear why certain fields are excluded from comparison

Compare API Responses Now

Paste two JSON responses and see semantic differences instantly — keys are sorted, formatting normalized.

Open JSON Diff →