JSON Diff Tool avatar

JSON Diff Tool

Pricing

Pay per event

Go to Apify Store
JSON Diff Tool

JSON Diff Tool

Semantically compare two JSON objects or files. Outputs a structured diff with dot-notation paths for every added, removed, changed, and type-changed field. Supports nested objects, arrays, URL fetching, and ignore lists.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Stas Persiianenko

Stas Persiianenko

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

Semantically compare two JSON objects or JSON files and get a structured, machine-readable diff — showing every added, removed, changed, and type-changed field with its exact dot-notation path.


🔍 What does it do?

JSON Diff Tool takes two JSON inputs (inline text or URLs to JSON endpoints) and produces a semantic diff — a structured list of every difference between them. Unlike text-diff tools that compare line by line, this actor understands JSON structure: it recurses into nested objects, tracks array indices, detects type changes (e.g. number → string), and outputs each change as a clean, queryable dataset row.

Every change is reported with:

  • Change type: added, removed, changed, or type-changed
  • Dot-notation path: e.g. address.zip, users[2].email, config.retries
  • Value A (before) and Value B (after)
  • Type A and Type B (e.g. number, string, boolean, object, array, null)

👤 Who is it for?

Developers and QA engineers who need to compare API responses between environments (staging vs production) to catch regressions before they reach users.

Data engineers who ingest JSON config files or API snapshots and need to audit what changed between two versions.

DevOps and release engineers who track changes in JSON configuration files (feature flags, infrastructure manifests, app settings) as part of change management workflows.

Product managers and analysts who receive JSON exports from tools (Mixpanel, Amplitude, Salesforce) and need to understand what records changed between two data pulls.


💡 Why use it?

  • Semantic, not textual — compares JSON meaning, not raw text. Whitespace and key ordering differences don't create false positives.
  • Structured output — each change is a dataset row with changeType, path, valueA, valueB, typeA, typeB. Filter, sort, and query with the Apify dataset API.
  • Nested objects and arrays — recurse arbitrarily deep. Array changes are tracked by index (or by content if you enable unordered comparison).
  • Type-change detection — flags when a value changes type (e.g. "42"42), which breaks APIs silently.
  • Flexible inputs — inline JSON text or any HTTPS URL serving JSON (APIs, S3 buckets, GitHub raw files, CDN endpoints).
  • Ignore keys — exclude volatile fields like updatedAt, requestId, or sessionToken from comparison.
  • Two output modesflat (one row per change, great for Apify tables) or summary (single row with all changes grouped, great for webhook payloads).
  • No proxy, no scraping — pure utility. No bandwidth costs beyond a simple HTTP fetch.

📊 Data you get

FieldTypeDescription
changeTypestringadded, removed, changed, type-changed, or identical
pathstringDot-notation path to the changed field (e.g. user.address.zip)
valueAstringSerialized value from JSON A (before/left), or null if added
valueBstringSerialized value from JSON B (after/right), or null if removed
typeAstringJSON type of value A: string, number, boolean, object, array, null
typeBstringJSON type of value B

In summary mode, a single record also includes totalChanges, added, removed, changed, typeChanged, and identical (boolean).


💰 How much does it cost to diff two JSON files?

Pricing is per-run (pay-per-event):

EventFREE tierDIAMOND tier
Run start$0.005$0.0025
Diff computed$0.001$0.0005

Typical cost per comparison: $0.006 (FREE tier) — less than $0.01 per diff regardless of JSON size or depth.

There is no per-item charge based on number of differences found. You pay a flat fee per comparison run. For high-volume automated workflows (hundreds of diffs per day), DIAMOND tier pricing applies automatically.

Free plan: Apify's free tier includes enough credits to run dozens of comparisons per month at no cost.


🚀 How to use it

Step 1: Open the actor

Go to JSON Diff Tool on Apify Store and click Try for free.

Step 2: Provide your two JSON sources

You have four options:

  • Paste JSON A directly into the JSON A (inline) field
  • Enter a URL in URL A (fetched at runtime — ideal for API endpoints)
  • Mix: inline for one, URL for the other

Step 3: Choose output mode

  • Flat (default): one dataset row per change — easiest to filter in Apify Console
  • Summary: single row with all changes grouped — ideal for webhook integration

Step 4: Configure options (optional)

  • Ignore array order: enable to compare arrays as sets (useful for tags, permissions lists)
  • Ignore keys: exclude volatile keys like updatedAt, requestId, etag
  • Max depth: limit recursion for very large deeply nested objects

Step 5: Run and inspect results

Results appear in the Dataset tab. Use the Apify API or export to JSON, CSV, or Excel.


⚙️ Input parameters

ParameterTypeRequiredDefaultDescription
jsonAstringOne of jsonA/urlAFirst JSON object as inline text
urlAstringOne of jsonA/urlAURL to fetch first JSON from
jsonBstringOne of jsonB/urlBSecond JSON object as inline text
urlBstringOne of jsonB/urlBURL to fetch second JSON from
outputModeselectNoflatflat (one row/change) or summary (grouped)
ignoreArrayOrderbooleanNofalseTreat arrays as unordered sets
ignoreKeysarrayNo[]Dot-notation paths to ignore (e.g. meta.updatedAt)
maxDepthintegerNo0 (unlimited)Max recursion depth (0 = unlimited)

📤 Output examples

Flat mode (default)

[
{ "changeType": "removed", "path": "active", "valueA": "true", "valueB": null, "typeA": "boolean", "typeB": null },
{ "changeType": "added", "path": "score", "valueA": null, "valueB": "42", "typeA": null, "typeB": "number" },
{ "changeType": "changed", "path": "age", "valueA": "30", "valueB": "31", "typeA": "number", "typeB": "number" },
{ "changeType": "added", "path": "address.country", "valueA": null, "valueB": "UK", "typeA": null, "typeB": "string" },
{ "changeType": "changed", "path": "address.zip", "valueA": "SW1A", "valueB": "SW1B", "typeA": "string", "typeB": "string" },
{ "changeType": "changed", "path": "tags[1]", "valueA": "user", "valueB": "superuser", "typeA": "string", "typeB": "string" }
]

Summary mode

{
"totalChanges": 6,
"added": 2,
"removed": 1,
"changed": 3,
"typeChanged": 0,
"identical": false,
"changes": [ ... ]
}

Identical JSON

When both inputs are identical, flat mode outputs a single row with changeType: "identical" so your downstream integrations always receive at least one row.


💡 Tips and tricks

Comparing API staging vs production: Enter your staging endpoint in URL A and production in URL B. Schedule daily runs and use webhooks to alert on unexpected changes.

Ignoring timestamps: Add updatedAt, meta.requestId, and _etag to the Ignore keys list to avoid noise from auto-updated fields.

Detecting type regressions: Type-changed entries (e.g. price changing from number to string) are your highest-priority changes — they silently break downstream consumers.

Large JSON objects: Use maxDepth: 3 to get a shallow diff of top-level and second-level changes first, then drill down to specific paths if needed.

Array order sensitivity: Enable Ignore array order for tags, roles, or permission lists where order doesn't matter. Keep it disabled for ordered lists like steps, timelines, or ranked results.


🔗 Integrations

Slack alerts for API drift: Connect to Zapier or Make (Integromat) — when totalChanges > 0 in the dataset, trigger a Slack message listing the changed paths.

GitHub Actions CI/CD: Call this actor via the Apify API in your deployment pipeline to diff your API's response schema between the new build and main branch before merging.

Webhook-driven monitoring: Set a webhook on actor completion, receive the summary payload, and route to PagerDuty if any type-changed events are detected.

Data pipeline validation: In your ETL, call this actor to diff two JSON exports before writing to your data warehouse, catching upstream schema drift early.

Configuration change auditing: Store your app's JSON config in S3 or GitHub. Point URL A to the previous version and URL B to the latest — run nightly to produce a change log.


🤖 API usage

Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('automation-lab/json-diff-tool').call({
jsonA: JSON.stringify({ name: 'Alice', age: 30, active: true }),
jsonB: JSON.stringify({ name: 'Alice', age: 31, active: false }),
outputMode: 'flat',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log('Changes:', items);

Python

from apify_client import ApifyClient
import json
client = ApifyClient(token='YOUR_APIFY_TOKEN')
run = client.actor('automation-lab/json-diff-tool').call(run_input={
'jsonA': json.dumps({'name': 'Alice', 'age': 30, 'active': True}),
'jsonB': json.dumps({'name': 'Alice', 'age': 31, 'active': False}),
'outputMode': 'flat',
})
items = client.dataset(run['defaultDatasetId']).list_items().items
for change in items:
print(f"{change['changeType']}: {change['path']}{change['valueA']}{change['valueB']}")

cURL

curl -X POST \
"https://api.apify.com/v2/acts/automation-lab~json-diff-tool/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonA": "{\"name\": \"Alice\", \"age\": 30}",
"jsonB": "{\"name\": \"Alice\", \"age\": 31}",
"outputMode": "flat"
}'

🧩 Use with Claude and MCP

Connect JSON Diff Tool to Claude for conversational JSON comparison and analysis.

Claude Code (terminal)

$claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/json-diff-tool"

Claude Desktop / Cursor / VS Code

Add to your MCP configuration JSON:

{
"mcpServers": {
"apify": {
"type": "http",
"url": "https://mcp.apify.com?tools=automation-lab/json-diff-tool",
"headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
}
}
}

Example prompts for Claude

  • "Diff these two JSON configs and tell me which keys changed type"
  • "Compare the API response from staging vs production at these two URLs"
  • "Run a JSON diff ignoring the updatedAt and requestId fields"
  • "Summarize the differences between these two JSON objects in plain English"

⚖️ Legality and data handling

This actor processes only the JSON data you provide — either inline or via URLs you specify. It does not scrape websites, bypass authentication, or access any systems without explicit permission.

  • Inline JSON is processed in memory and never persisted beyond the run dataset.
  • URL-fetched JSON is retrieved with a standard HTTP GET request from Apify's infrastructure. Only fetch URLs you are authorized to access.
  • No login credentials, cookies, or session tokens are used.
  • No proxy is used. All HTTP requests are direct datacenter-originated calls.

This actor is intended for comparing JSON you own or have permission to access. Use it responsibly.


❓ FAQ

Does it support JSON arrays at the top level? Yes. If your JSON is a top-level array (e.g. a list of objects), the actor compares by array index by default. Use ignoreArrayOrder: true for content-based comparison.

What happens if a URL is not reachable? The run will fail with a clear error message showing the URL and HTTP status code. Check that the URL is publicly accessible and returns Content-Type: application/json or plain JSON text.

How does it handle very large JSON (megabytes)? The actor loads both JSON objects into memory at 256 MB. Very large JSON files (> 50 MB) may cause memory issues — use maxDepth to limit recursion for oversized payloads. For datasets with thousands of records, consider splitting your comparison.

What does type-changed mean vs changed? changed means the value changed but remained the same type (e.g. 3031). type-changed means both the value AND the type changed (e.g. 42"42" = number to string). Type changes are particularly important to catch because they silently break downstream consumers.

My arrays have the same items but in different order — is that a change? By default yes — array order is significant. Enable ignoreArrayOrder to treat arrays as unordered sets. Note: this uses serialization-based matching, so it works best for primitive arrays (strings, numbers). Object arrays use exact-match semantics.

Why does flat mode output an "identical" row when there are no differences? To ensure your downstream integrations always receive at least one row in the dataset. An empty dataset can be ambiguous — the identical row makes the "no differences" case explicit and queryable.