API Response Diff: Compare JSON API Payloads Online
Paste the response you expected on the left, the response you actually got on the right, and see every field that changed. Built for backend, QA, and integration work. Nothing leaves your browser.
What this API response diff tool is
A free, in-browser tool for comparing two HTTP API response bodies. Paste the JSON you got from staging on the left, the JSON you got from production on the right, and the differences highlight character by character. The text never leaves your machine, which matters because real API responses regularly contain customer email addresses, session tokens, internal user IDs, and other things you do not want to upload to a third-party diff site.
It is built for the moment a flaky integration test fails in CI, and you have a Postman screenshot of a working response on your laptop and a CI log of a broken response from the build runner. Spinning up a full Pact contract test for a one-off investigation is overkill. Two text panes, one diff, and you can usually narrow it down to a single field in under a minute.
Underneath, the diff itself is the same engine that powers our compare-json tool. We have just framed it for the API testing workflow. If your responses are XML or SOAP envelopes, our compare-xml page handles those. If you are diffing free-form text such as a webhook log or an audit trail, our compare-text tool is the right surface.
How an API response diff actually helps
An API response diff sits in the gap between two related but distinct testing ideas. OpenAPI 3.1 schema diff tells you what your contract says changed: a new optional field, a renamed property, a stricter enum. Snapshot testing (Jest snapshots, Vitest snapshots, pytest-snapshot) tells you what your code produced changed against a saved fixture. This tool sits on the runtime side. You give it two real response bodies and it shows you every byte that differs, regardless of whether the schema permits the change or whether your snapshot fixture is up to date.
Why is that useful? Because the bugs that bite hardest in REST integration work are not schema violations. They are subtle drifts: a serializer that quietly switched a date from ISO-8601 to a Unix timestamp after a Jackson upgrade, a Marshmallow schema that started emitting null instead of omitting a missing field, a DRF ViewSet that began wrapping the payload in a data envelope after a middleware change. The OpenAPI spec did not change. The snapshot was not updated. The tests passed in isolation. The integration broke. A response-body diff catches all of these because it does not care about the contract; it cares about the bytes.
Volatile fields are the main thing to watch for. Timestamps, request IDs, trace IDs, server-generated UUIDs, and pagination cursors will differ between any two captures of the same endpoint, even when nothing meaningful has changed. The right move is to normalize before diffing: replace timestamps with a placeholder, strip trace IDs, sort arrays where order is not contractually significant. Tools like Pact handle this with matchers; in this tool you handle it by editing the panes. It takes ten seconds and removes the noise floor.
How to diff an API response in three steps
Two text panes, one diff. No login, no upload, no proxy to wire up.
- 1
Capture the first response
Hit the endpoint with curl, httpie, Postman, Insomnia, or whatever your team uses. curl -s https://api.example.com/v1/users/123 | jq is a good baseline because jq pretty-prints the JSON, which makes the diff far easier to read. Copy the body (just the JSON, not headers) and paste it into the left pane. If you are pulling from a CI log, strip the timestamp prefix that most loggers add so the diff lands on the actual payload.
- 2
Capture the second response
Hit the comparison source: the other environment, the other API version, the other vendor. Same shape of capture, same pretty-print step. Paste it into the right pane. If one capture is from a recorded fixture (vcrpy, pollyjs, MSW, nock) and the other is live, normalize obvious volatile fields first: scrub the request_id, replace timestamps with a constant, and remove any trace headers that leaked into the body. The remaining diff is the actual signal.
- 3
Read the highlighted differences
Deletions show as red strikethroughs on the left; insertions show as green on the right. The change counters in each header tell you how many distinct edits the diff found. Focus on three things first: status string changes, missing or added keys, and value type changes (string to number, object to null). Those three categories cover almost every real API regression. Format and ordering changes are usually noise unless your consumer depends on them.
When an API response diff is the right call
Reproducing a flaky integration test
A test passes locally and fails in CI. You have the response captured by your local Postman run and the response captured by the CI build agent (most CI systems can dump request/response with a verbose flag). Paste both into the diff tool. Nine times out of ten the difference is something environmental: a different feature flag, a stale fixture, a timezone offset on the build runner. The remaining one in ten is a real bug, and you have just localised it to a specific field.
Validating a fixture against a fresh response
Your repo has a checked-in fixture file that mocks a third-party API for tests. The upstream provider just shipped a new minor version. Hit the live endpoint with curl, paste that response next to your fixture, and you see exactly which fields drifted. This is the manual version of what VCR-style replay tools automate. Useful when you want to update one fixture without rerecording your entire test suite.
Verifying API version backward compatibility
You are about to ship v2 of an internal API. A v1 client still exists in production. Hit both /v1/orders/42 and /v2/orders/42 and diff the responses. Any removed field, any renamed key, any value-type change is a breaking change for the v1 client. Any new field is additive and safe. This is a poor person's consumer-driven contract test; it does not scale to dozens of endpoints, but for a quick sanity check on one or two it works.
Spotting a serializer regression
Your team upgraded Jackson, Marshmallow, DRF, or a similar serialization layer. Tests pass. Then a downstream consumer reports their parser is choking. Capture the same endpoint response on the old branch and the new branch and diff them. Common findings: a date format flipped from 2026-05-09T10:00:00Z to a Unix timestamp, decimals lost trailing zeros, an enum started serializing as integer instead of string, or null fields began appearing in the payload that were previously omitted.
Comparing webhook payloads from two providers or versions
Stripe webhook V1 and V2 of the same event type look almost identical and are very much not. So do GitHub webhook event payloads across API versions, and Slack event subscriptions across scopes. Paste a sample payload from each into the diff tool to see the renamed fields, the moved nested objects, and the new metadata blocks. This is faster than reading both provider doc pages side by side, especially when the docs gloss over which fields actually appear in practice.
Debugging "works on staging, broken on prod"
The classic deploy headache. The same client request returns subtly different JSON from staging and production. Capture both, paste both, and the difference is usually a config flag, a missing feature gate, or a stale cached response. This applies equally well to multi-region debugging (us-east-1 vs eu-west-1 returning different data), CDN caching issues (Cloudflare cached a stale body), and read-replica lag where a write has not propagated everywhere.
API response diff edge cases worth knowing
The cases where a response-body diff disagrees with what your test framework, your OpenAPI tool, or your eyes would say. Worth scanning before you assume the diff has found a real bug.
| Topic | What this tool does |
|---|
| Volatile fields (timestamps, IDs) | created_at, updated_at, request_id, trace_id, server-generated UUIDs, and pagination cursors will always differ across two captures. Normalize them with jq 'del(...)', replace with a constant, or just edit them out before diffing. The signal you actually care about lives elsewhere. |
|---|
| null vs missing key | {"foo": null} and {} are different in JSON, and many serializers (Marshmallow, Jackson, System.Text.Json) have settings that flip between them. The diff will surface this. Some clients treat them as equivalent and some do not; the right answer is whatever your consumer does, which is why this is a frequent regression source after a serializer upgrade. |
|---|
| Key ordering | RFC 8259 defines JSON objects as unordered. Two semantically identical responses with different key orders will show as a diff here because text comparison is order-sensitive. Pre-sort both sides with jq --sort-keys if you want order-insensitive diffing. Watch out for the rare consumer that does depend on order (some signing flows, some legacy parsers). |
|---|
| Array ordering | Arrays in JSON are ordered, but many APIs return arrays whose order is not actually contractual: a list of permissions, a list of feature flags, a list of webhook subscriptions. A diff will flag a reordered array as a change even when no consumer cares. If reordering is harmless in your domain, sort both sides on a stable key before diffing. |
|---|
| JSON.parse strictness | The diff treats your input as opaque text. If one of your captures has a trailing comma, an unquoted key, or a comment (all illegal in strict JSON), it will still diff but it will look noisier than it should. Run both captures through jq . first to reformat and reject invalid input. jq uses a strict RFC 8259 parser. |
|---|
| Response wrapping (data envelope vs flat) | Many APIs wrap payloads in a {"data": ...} envelope, sometimes adding meta, links, or included alongside (JSON:API and similar). A migration from flat to wrapped (or vice versa) is a breaking change that shows up immediately in a diff but is easy to miss in a schema review because the underlying record looks the same. |
|---|
| Pagination cursor changes | Cursor-based pagination uses opaque tokens (next_cursor, after, page_token) that are designed to change every call. They will always show as a diff. Strip them before comparing, or compare only the data array contents and ignore the pagination block entirely. |
|---|
| Error response formats | Error bodies are the wild west. Some APIs return a flat {"error": "..."}, some return RFC 7807 Problem Details with type, title, status, detail, instance, and some return a vendor-specific shape. A migration to RFC 7807 from a custom shape will produce a large diff that is mostly an improvement; a migration the other way is a regression worth catching early. |
|---|
API response diff: frequently asked questions
How is this different from Postman's built-in diff feature?
Postman has a response comparison view inside its Collection Runner and a Visualize feature for individual responses. Both are good if you live in Postman and your responses are already saved as Postman history items. This tool is provider-agnostic. You can paste from Postman, Insomnia, curl, httpie, a CI log, a Stack Overflow snippet, or a colleague's Slack message. There is no account, no workspace, no sync. For one-off comparisons across tools, that is faster. For team-shared API testing inside a single platform, Postman's own feature is fine.
How do I handle volatile fields like timestamps and request IDs?
The pragmatic move is to normalize before diffing. Open both pastes in the panes, then edit out the volatile fields directly: replace timestamps with a constant string, strip request_id and trace_id values, remove pagination cursors that change every call. The diff highlights only the remaining differences, which are the ones that actually matter. For repeated comparisons of the same endpoint, you can also pipe the response through jq with a delete filter (jq 'del(.meta.request_id)') before pasting.
How is this different from an OpenAPI schema diff?
Schema diff compares contracts: it tells you that POST /orders added an optional discount_code field, or that the status enum gained a new value. OpenAPI-aware tools like oasdiff or Spectral do this well. This tool compares actual response bodies. The two are complementary. Schema diff catches contract changes; response diff catches drift between the contract and reality, which is where serialization bugs, environment mismatches, and stale fixtures hide.
Can it handle large responses?
Practically yes, up to a few thousand lines of pretty-printed JSON on each side. Beyond that the character-level diff with semantic cleanup gets slow because it runs in your browser, not on a server. For very large payloads (think a paginated dump of 10,000 records), the right approach is to slice the response into smaller pieces by record or by top-level key and diff each piece separately. Or run a structural diff on the command line with jd or diff <(jq . a.json) <(jq . b.json) for raw speed.
Does it work for XML or SOAP responses?
Not directly. This page is tuned for JSON, which is what most modern REST and webhook payloads use. If you need to diff XML, SOAP envelopes, RSS, or POM-style configuration, our compare-xml tool is the right surface; it handles indentation and namespace formatting correctly. For raw response bodies that mix headers and body, or for plain-text APIs (some legacy systems still return text/plain), compare-text does the job without trying to enforce a structure.
Does it preserve key order or sort keys?
It preserves the key order you paste. JSON objects are formally unordered per RFC 8259, so two semantically identical responses with keys in different orders will show as a diff in this tool. If you want to ignore ordering, normalize both sides first by piping through jq --sort-keys or an equivalent. Most clients do not depend on key order, so sorted-key normalization is a safe default for response comparison; just be aware some legacy consumers (older XML-to-JSON bridges, certain digital-signature flows) do care about order.
Does this work for GraphQL responses?
Yes. GraphQL responses are JSON envelopes with the standard { data, errors, extensions } shape, so the diff just works. The wrinkle is that the client controls which fields come back and in what order; the same query reissued with reordered selections produces a textually different response that is semantically the same. Arrays inside data are also not guaranteed to preserve order across resolvers. Volatile fields are usually scoped to extensions (tracing, request IDs, cache hints), so strip those before diffing. For schema-level changes, reach for a dedicated tool like graphql-inspector rather than this one.
Can I diff a gzipped or protobuf response body?
Not directly. Both are binary. Pasting gzipped bytes will look like garbage, and a Protobuf message has no canonical text form because field tags are integers and types depend on the .proto definition. For gzip, ungzip first with gunzip < file | jq ., or in Chrome DevTools open the Network tab and use the Response view, which shows the decoded body. For Protobuf, run protoc --decode_raw for a structure-only view, or grpcurl with the matching .proto for a typed text rendering. Once you have human-readable text, paste both sides into the diff.
Privacy and why it matters for API responses
API response payloads regularly contain things you do not want to leak. Customer email addresses. Internal user IDs. Session tokens. Auth bearer tokens that ended up in a body field by mistake. Stripe customer IDs. Webhook secrets. PII fields like names, addresses, and phone numbers. Pasting one into a cloud-hosted diff service is itself a data-handling event, and depending on your industry it may violate your SOC 2 controls, GDPR data-processing agreements, or HIPAA Business Associate Agreements. This tool runs entirely in your browser. Nothing is uploaded, logged, or sent to any third-party service. The diff, the highlighting, and the rendering all execute on your machine. Verifying the claim is straightforward: open your browser's DevTools, switch to the Network tab, paste both responses, and watch. There are no outbound requests when you compare. For broader API design and security guidance, Microsoft's API design best practices are a solid reference.