Diff package.json dependencies
Compare two dependency lists from package.json and see what really moved: react goes from ^17.0.2 to ^18.2.0, axios is added, moment is removed and lodash stays the same. Formatting and key order are ignored. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
Comparing dependency blocks between two package.json snapshots is a daily chore for release reviews, dependency upgrades, and pull-request audits. A raw text or line diff mixes noise from formatting, key order, and unrelated fields with the signal you care about: which packages appeared, which left, and which version ranges moved. This package.json dependency diff accepts two plain objects—each a map of package name to version range string, exactly as you find under dependencies, devDependencies, peerDependencies, or optionalDependencies—and returns structured lists of added, removed, and changed packages plus unchanged entries and counts.
What a semantic dependency diff reports
A semantic dependency diff treats each package name as a set element and each version field as a string label attached to that name. The baseline map is before; the candidate map is after. Any name present only in after is reported under added with its new version range.
Any name present only in before is reported under removed with the version it had. Names present in both maps with unequal version strings appear under changed with from and to fields. Names with identical version strings appear under unchanged so you can still inventory the stable bulk of the lock surface.
Counts for each bucket and totals for both maps let dashboards render badges without recounting arrays. The comparison is intentionally shallow: it does not expand caret ranges, resolve the npm registry, or evaluate whether ^17.0.0 and 17.0.2 would install the same tree. That keeps the tool deterministic, offline, and suitable for CI gates that only need to know whether the declared dependency object moved.
Each must be a plain object, not an array, not null, and not a primitive. Package names become object keys; version ranges must be strings or simple scalars that stringify cleanly—nested objects as version values are rejected. Empty objects are valid and mean an empty dependency set.
On success the body includes added, removed, changed, and unchanged arrays sorted alphabetically by package name; each added or removed item has name and version, each changed item has name, from, and to, and each unchanged item has name and version. Example: before react ^17.0.2, lodash ^4.17.21, moment ^2.29.4 versus after react ^18.2.0, lodash ^4.17.21, axios ^1.6.0 yields moment removed, axios added, react changed from ^17.0.2 to ^18.2.0, and lodash unchanged.
Where a package.json dependency diff earns its keep
Release managers freeze a known-good dependency object before a upgrade wave and re-diff after automated Dependabot or Renovate batches to confirm the intended set of bumps. Security review pipelines pair this structured output with advisory lookups: only the union of added and changed package names needs CVE scanning when the rest of the tree is unchanged. Keep maps modest, pin the sorted arrays in tests, and you get reproducible dependency change reports for CI, code review, and migration checklists without standing up a full lockfile analyzer.
What you can do with it
Pull-request dependency summaries
Diff base and head package.json dependency objects and post only added, removed, and version-changed packages on the PR.
Upgrade and Renovate batch review
Compare the dependency map before and after an automated upgrade batch to verify intended bumps and spot surprise removals.
Focused security re-scan lists
Feed added and changed package names into advisory scanners so unchanged packages are not re-checked on every commit.
FAQ
How do I diff two package.json dependency objects?
Send before and after as plain objects mapping package names to version range strings.
Does this resolve semver ranges against the registry?
No. Version specs are compared as exact strings. ^1.0.0 and 1.0.0 are treated as a change even if both might install the same release.
Can I pass devDependencies or peerDependencies?
Yes. Any name→version map works—dependencies, devDependencies, peerDependencies, optionalDependencies, or a merged object you build yourself.
What happens if before or after is not an object?
Arrays, null, strings, and missing fields are rejected and not billed.
Why not just compare the files line by line?
A line diff mixes noise from formatting, key order and unrelated sections with what matters. Here each package name is compared with its version text, so you only see packages that appeared, left or changed version.
How does it help with security reviews?
When most of the tree is unchanged, only added and changed packages need a new advisory check. Feed those names to your scanner instead of re-checking every package on every commit.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/semantic-diff-package-json \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"before":{"react":"^17.0.2","lodash":"^4.17.21","moment":"^2.29.4"},"after":{"react":"^18.2.0","lodash":"^4.17.21","axios":"^1.6.0"}}'const res = await fetch("https://api.kit.forhosting.com/dev/semantic-diff-package-json", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"before": {
"react": "^17.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.4"
},
"after": {
"react": "^18.2.0",
"lodash": "^4.17.21",
"axios": "^1.6.0"
}
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/semantic-diff-package-json",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"before": {
"react": "^17.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.4"
},
"after": {
"react": "^18.2.0",
"lodash": "^4.17.21",
"axios": "^1.6.0"
}
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/semantic-diff-package-json", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"before":{"react":"^17.0.2","lodash":"^4.17.21","moment":"^2.29.4"},"after":{"react":"^18.2.0","lodash":"^4.17.21","axios":"^1.6.0"}}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"before":{"react":"^17.0.2","lodash":"^4.17.21","moment":"^2.29.4"},"after":{"react":"^18.2.0","lodash":"^4.17.21","axios":"^1.6.0"}}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/semantic-diff-package-json", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"before": {
"react": "^17.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.4"
},
"after": {
"react": "^18.2.0",
"lodash": "^4.17.21",
"axios": "^1.6.0"
}
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.semantic_diff_package_json",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Non-object inputs fail with invalid_input and are not billed.
What a semantic dependency diff reports
Paste dependencies, devDependencies, or a merged view you built yourself—the endpoint only needs two objects of string-valued version specs.
Input contract, response shape, and errors
POST a JSON body with two required object fields: before (aliases left, old, previous) and after (aliases right, new, next). Numeric counters added_count, removed_count, changed_count, unchanged_count, total_before, and total_after mirror the arrays. The boolean has_changes is true when any package was added, removed, or version-changed. If before or after is missing, an array, a string, or otherwise not an object, the task fails with invalid_input naming the field; those failures do not consume balance.
Where a package.json dependency diff earns its keep
Pull-request bots can extract dependencies from base and head package.json files, call this endpoint, and post a short comment that lists only real package moves instead of a noisy unified diff.
Technical questions
What does the package.json dependency diff cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.