JSON diff API

Compare two documents and list what was added, removed and changed.

EndpointPOST https://astraljson.com/api/v1/json-compare
AuthBearer token, free
Sendsinputs: text[]
Returnsresult: json
PriceFree. 1,000 calls a day, 60 a minute.

When to call it

A text diff of two JSON files reports a reformat as a change and a reordered object as a rewrite. This compares the structures, so a test that asserts on an API response, or a job watching a config for real drift, only fires when something actually moved.

For a one-off, the browser version needs no token and never sends your data anywhere: it runs on your own machine.

Example

curl
curl -X POST https://astraljson.com/api/v1/json-compare \
  -H "Authorization: Bearer ast_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputs": ["{\"plan\":\"free\",\"limits\":{\"calls\":100}}","{\"limits\":{\"calls\":1000},\"plan\":\"free\",\"beta\":true}"]}'
response
{
  "tool": "json-compare",
  "result": "{\n  \"identical\": false,\n  \"added\": 1,\n  \"removed\": 0,\n  \"changed\": 1,\n  \"changes\": [\n    {\n      \"path\": \"beta\",\n      \"type\": \"added\",\n      \"after\": true\n    },\n    {\n      \"path\": \"limits.calls\",\n      \"type\": \"changed\",\n      \"before\": 100,\n      \"after\": 1000\n    }\n  ]\n}",
  "chars": 276,
  "ms": 1
}

In your language

JavaScript
const res = await fetch("https://astraljson.com/api/v1/json-compare", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ASTRAL_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"inputs": ["{\"plan\":\"free\",\"limits\":{\"calls\":100}}","{\"limits\":{\"calls\":1000},\"plan\":\"free\",\"beta\":true}"]}),
});
if (!res.ok) throw new Error((await res.json()).detail);
const { result } = await res.json();
Python
import os, requests

res = requests.post(
    "https://astraljson.com/api/v1/json-compare",
    headers={"Authorization": f"Bearer {os.environ['ASTRAL_TOKEN']}"},
    json={"inputs": ["{\"plan\":\"free\",\"limits\":{\"calls\":100}}","{\"limits\":{\"calls\":1000},\"plan\":\"free\",\"beta\":true}"]},
    timeout=30,
)
res.raise_for_status()
result = res.json()["result"]

Parameters

FieldTypeNotes
inputsstring[]Required. Between 2 and 20 items. Up to 200,000 characters per call.

Errors

Errors are RFC 7807 problem documents with a stable type you can branch on.

missing-token401No Authorization header.
bad-request400inputs missing or the wrong type.
tool-failed422The input could not be processed. detail says why.
quota-exceeded429Past 1,000 calls today.

Questions

Why does this need inputs instead of input?
It compares two documents, so it reads an "inputs" array rather than a single "input". Send exactly two items; both count together against the size limit.
Does key order count as a difference?
No. Objects are compared by structure, so reordering keys reports nothing. Array order does count, because order is meaningful in a JSON array.
What does a change look like?
Each entry carries a path such as limits.calls, a type of added, removed or changed, and the before and after values. The report also gives counts and an "identical" flag you can branch on.

Other endpoints

A token takes thirty seconds

Free, no card, 1,000 calls a day, and the same token works on astraltext.com, astralpdf.com, astraljson.com and astralbatch.com.

Get a free token