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?
- Regression testing — Ensure an API update didn't break existing behavior
- Staging vs Production — Verify environments return consistent data
- API migration — Compare old and new endpoints during migration
- Debugging — Spot exactly what changed when something breaks
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:
- Key ordering —
{"a":1,"b":2}and{"b":2,"a":1}are semantically identical - Formatting — Minified vs pretty-printed JSON looks different but means the same thing
- Dynamic fields — Timestamps, request IDs, and session tokens change every call
- Array ordering — Sometimes order matters (pagination), sometimes not (tags)
Method 1: Quick Visual Comparison
For one-off checks, paste both responses into a JSON diff tool:
- Call both endpoints (e.g., with
curlor Postman) - Copy the response bodies
- Paste into a JSON diff tool like DiffSnap JSON Diff
- 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.jsonThe -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.jsonMethod 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.jsonFloating 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.jsonPagination Differences
When comparing paginated endpoints, fetch all pages first, then concatenate and compare the full dataset.
Best Practices
- Normalize before diffing — Sort keys, remove dynamic fields, format consistently
- Use semantic diff for JSON — Don't rely on line-by-line text diff
- Automate in CI — API regression tests catch breaking changes before deployment
- Version your baselines — Store snapshot files in git alongside tests
- 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 →