APIs change silently. A field gets renamed. A type flips from string to integer. A key disappears. A number drifts 15% without anyone noticing.
apicmp compares two JSON responses — from URLs, files, or stdin — and tells you exactly what changed: fields added, fields removed, type changes, value changes, and numeric drift beyond a threshold.
No auth setup. No config files. No enterprise pricing. Just point it at two things and see the diff.
pip install apicmpOr with pipx:
pipx install apicmp# Two live URLs
apicmp https://api.example.com/v1/user https://api.example.com/v2/user
# Two local files
apicmp before.json after.json
# File vs live URL
apicmp snapshot.json https://api.example.com/endpoint
# Pipe first response from stdin
curl https://api.example.com/v1 | apicmp - https://api.example.com/v2
# Flag numeric drift over 10% (default is 5%)
apicmp a.json b.json --threshold 0.1
# Add auth or custom headers
apicmp a.json b.json -H "Authorization: Bearer TOKEN"
apicmp a.json b.json -H "X-Api-Key: abc123"
# Export diff as Markdown
apicmp a.json b.json --output diff.md
# JSON output — pipe to jq
apicmp a.json b.json --format json | jq '.added'apicmp flattens nested JSON to dot-notation paths and compares them structurally:
| Category | What it catches |
|---|---|
| Added | Keys present in B but not in A |
| Removed | Keys present in A but not in B |
| Type changed | Same key, different type (string → integer, null → object) |
| Value changed | Same key, same type, different value |
| Numeric drift | Numbers that changed beyond your threshold (default 5%) |
Nested objects are flattened — user.address.city is a single comparable path. Arrays are compared by index up to 20 elements, and array lengths are tracked separately.
--format table (default) — colour-coded rich terminal tables, one per change category.
--format markdown — GitHub-flavored Markdown, paste into PRs, wikis, or incident reports.
--format json — machine-readable, pipe to jq or use in scripts.
Arguments:
SOURCE_A First JSON source (URL, file, or '-' for stdin).
SOURCE_B Second JSON source (URL or file).
Options:
-t, --threshold Flag numeric values that differ by more than this fraction
(default: 0.05 = 5%).
-H, --header Extra HTTP headers (format: 'Name: Value'). Repeatable.
--timeout HTTP request timeout in seconds (default: 15).
-o, --output Write Markdown diff to this file.
-f, --format Output format: table | markdown | json (default: table).
--help Show this message and exit.
Snapshot an API before a deploy, diff after:
curl https://api.example.com/user/1 > before.json
# deploy...
apicmp before.json https://api.example.com/user/1Compare staging vs production:
apicmp https://staging.api.example.com/config \
https://api.example.com/config \
-H "Authorization: Bearer $TOKEN"Export diff for a PR comment:
apicmp before.json after.json --output diff.md
cat diff.mdUse in CI — fail if APIs diverge:
apicmp snapshot.json https://api.example.com/endpoint \
--format json | python -c "
import json, sys
d = json.load(sys.stdin)
if d['added'] or d['removed'] or d['type_changed']:
sys.exit(1)
"git clone https://github.com/gitwingo/apicmp
cd apicmp
pip install -e .If apicmp has been useful to you, consider supporting its development:
- GitHub: @gitwingo
- Reddit: u/gitwingo
- X / Twitter: @gitwingo
MIT © gitwingo
