A free, browser-based set of JSON tools. Paste any JSON document and get every key path it contains - including keys buried inside nested objects and arrays - rendered as dot notation, JSONPath, an indented tree, or a generated TypeScript type; pretty-print and minify it without changing a single value; or compare two versions of it and see which changes would break the code that reads it.
Eight tools share one engine: Key Extractor (the homepage), Run cURL, Formatter, Diff, Flattener, JSONPath Generator, Tree Viewer, and JSON to TypeScript. Alongside them is a blog of tested, long-form guides on working with JSON.
Live at jsonkeyper.com.
Pasted JSON is processed entirely client side. It is never uploaded, logged, or stored, and that flow keeps working with the network disconnected. The one exception is the optional Execute cURL tab, which sends a request through a proxy to fetch a live API response (see Repository layout).
| Output format | What it gives you |
|---|---|
| Key paths | user.address.city, orders[0].total - paste straight into code |
| Key paths with value types | The same paths annotated string, number, object, ... |
| Unique key names | A sorted, deduplicated vocabulary of every key name |
| JSONPath | $.user.address.city, with [*] wildcards when indices are collapsed |
| Indented tree | A visual read of shape and nesting depth |
| TypeScript interface | A generated interface Root { ... } from your sample |
| Formatted / minified JSON | The JSON itself, re-indented (2 or 4 spaces) or on one line |
Alongside those:
- Collapse array indices - merges
items[0].skuanditems[1].skuinto a singleitems[].sku, which is what you want when documenting a schema rather than inspecting one record. - Structure statistics - total key paths, unique key names, maximum nesting depth, object and array counts, and payload size.
- Input options - paste, drag a
.jsonfile onto the input box, use the file picker, or load the built-in sample.Ctrl/Cmd+Enterruns the tool. - Execute cURL - paste a curl command to fetch a live API response through a
CORS proxy, preview the status, headers, and body, then extract keys from it.
It has its own page,
/execute-curl, and is also a tab on the homepage (/?mode=curlopens it). The parser handles-X,-H,-dand its variants,--data-urlencode,--json,-G,-I,-u,-A,-e, and-b, sending what curl 8.5 sends; options that read local files are refused with an explanation. A defaultUser-Agentis added when the command sets none. - Useful parse errors - reports the line and column of malformed JSON rather than a generic failure.
- Copy or download - to the clipboard, or as
json-keys.txt(.tsfor the TypeScript format,formatted.jsonfor the formatted and minified outputs). JSON Diff downloads a Markdown report (json-diff.md) or the patch (json-patch.json). - Moving between tools - a tab strip under the header on every tool page, and
a "JSON Tools" directory on the homepage. JSON already pasted comes along: it is
parked in
sessionStoragefor that tab, read once by the next tool, and deleted. On JSON Diff it arrives as the Original, ready for the second document.
The TypeScript generator merges objects across an array and marks any key absent
from some elements as optional, so a giftMessage present on only one of two
orders is emitted as giftMessage?: string. A key that is present but null on
some elements stays required and gains | null instead, and arrays nested in
those elements pool their items, so an empty labels: [] on one record does not
hide the element type seen on another.
JSON Diff (/json-diff, code in diff.js) compares two documents three ways:
a list of added, removed, and changed values; a structure diff that flags
breaking changes (a field removed, retyped, newly nullable, or newly optional);
and an RFC 6902 JSON Patch. Key order is ignored, array items are paired by an
identifying field (id, uuid, sku, ...) when every item has a unique one,
and a small parser keeps number literals and duplicate keys, so numbers are
compared exactly as written.
The formatted and minified outputs never re-serialise: formatJsonText walks the
original text and changes only whitespace outside strings, so integers above
2^53, trailing zeros such as 12.50, key order, and duplicate keys survive
exactly. JSON.parse still runs first, to report errors.
These are deliberate, and documented on the site itself:
- Type inference describes the sample you paste, not the API's real contract.
A field that happens to be
nullin your sample is typednull. - The full path list is built in memory, so documents in the tens of megabytes
may be slow. Use a streaming tool such as
jqat that scale. - JSON does not distinguish integers from floats, so every number is
number. - Dot-notation output is ambiguous for keys that themselves contain a dot. The JSONPath output quotes those instead.
- It validates that JSON parses; it does not validate against a JSON Schema.
- Execute cURL only reaches public
http/httpsURLs on ports 80 and 443, times out after 5 seconds, and rejects responses over 2 MB. - JSON Diff compares two documents; it does not merge three. Changed strings are
shown whole rather than character by character, and the patch uses
replacerather thanmovefor reordered arrays, which is correct but longer.
Pages are addressed without .html (/about, /blog/cors-explained): GitHub
Pages serves about.html for /about. Every internal link, canonical tag,
og:url, JSON-LD URL, and sitemap <loc> uses the clean form. GitHub Pages
cannot redirect, so each page (except 404.html) carries a small inline script
that rewrites an old .html address in the address bar with
history.replaceState, without reloading.
index.html Tool plus reference content, FAQ, and structured data
script.js Traversal, output formatters, and UI wiring (no dependencies)
diff.js JSON Diff: preserving parser, value/structure diff, JSON Patch, page UI
nav.js Navbar collapse toggle, loaded by every page
curl-parser.js Self-contained curl command string parser, no dependencies
curl-proxy.js Execute cURL tab: proxy request/response UI, wired to curl-parser.js
styles.css All custom styling; Bootstrap 4.5.2 CSS is loaded from a CDN
json-flattener.html Tool page: dot-notation key paths (payment webhook example)
json-to-typescript.html Tool page: TypeScript generation (issue-tracker example)
jsonpath-generator.html Tool page: JSONPath expressions (Kubernetes pod list example)
json-tree-viewer.html Tool page: indented structural tree (GeoJSON example)
json-formatter.html Tool page: pretty-print and minify (event feed example)
Each of the five above embeds the tool with its format
preselected, and its own sample in <script
id="pageSample">, which loadSample() loads verbatim in
place of the homepage sample (a parse/stringify round
trip would alter it)
execute-curl.html Tool page: the Execute cURL tab opened by default, with the
proxy's options, behaviour, and limits documented
json-diff.html Tool page: two-document diff with its own two-input UI
(diff.js); its payouts API v1/v2 example is in <script
id="diffExample">
about.html Author background and work history, how the guides are written
contact.html Contact form (composes a mailto:)
privacy.html Privacy policy
terms.html Terms of service
changelog.html User-facing change history, newest first
404.html Not-found page; GitHub Pages serves it automatically
blog/index.html Article listing
blog/*.html Seventeen long-form guides on working with JSON
og-image.png 1200x630 social card referenced by og:image on every page
sitemap.xml Kept in sync by hand when pages are added
robots.txt Allows all crawlers, points at the sitemap
ads.txt AdSense seller declaration
CNAME Custom domain for GitHub Pages
tools/serve.py Local server that resolves clean URLs like GitHub Pages
tools/check-sitemap.py Reports sitemap lastmod values older than the file's last commit
tools/diff-tests/ Tests for diff.js: node tools/diff-tests/test.js
tools/curl-tests/ Tests for curl-parser.js, checked against real curl
tools/json-vs-xml-bench/ Reproduces the measurements in blog/json-vs-xml.html
tools/model-mismatch/ Reproduces blog/null-missing-unknown-fields.html in
Jackson 2 and 3, Pydantic, and Zod (see its README)
There is no build step, no bundler, and no package manifest - the files served
are the files in the repository. The homepage and the seven tool pages load
script.js; the diff page also loads diff.js, and the homepage and
/execute-curl also load curl-parser.js and curl-proxy.js; every page loads
nav.js. Bootstrap's CSS is still used from a CDN, but its JavaScript, jQuery,
and Popper were removed because the navbar toggle was the only behaviour that
depended on them.
The homepage's Execute cURL tab sends the parsed request to a Cloudflare Worker
(jsonkeyper-worker, deployed separately - not in this repository) that proxies
the request server-side to work around browser CORS. It is the only feature on
the site that transmits data off the visitor's device; see the privacy
policy for what it does
and does not do with that request.
Internal links are root-relative and extensionless (/about, /blog/), so a
plain python3 -m http.server will 404 on them. Use the bundled server, which
resolves URLs the same way GitHub Pages does:
python3 tools/serve.py
# then open http://localhost:8000Pushes to main trigger .github/workflows/static.yml,
which publishes the whole repository to GitHub Pages. There is no staging
environment, so a merge to main goes live immediately. Work on a branch and
open a pull request.
Issues and pull requests are welcome. A few conventions worth knowing:
- Match the existing style. Vanilla JS with inline
onclickhandlers for the tool controls, four-space indentation, and no new runtime dependencies. - Use hyphens, not em dashes, in page copy.
- Link without
.html(/about, not/about.html), and use the same clean form in canonical tags,og:url, and JSON-LD. New pages also need the address-bar script from the top of any existing page's<head>. - Update
sitemap.xmlwhen you add or remove a page, and runpython3 tools/check-sitemap.pyto catch stalelastmodvalues. - Keep dates honest. Article
datePublished/dateModifiedvalues, visible bylines, and sitemaplastmodentries must reflect when the content was actually written. - Test the tool before opening a PR.
script.jsanddiff.jsexpose plain functions, so they can be exercised directly under Node without a browser. Runnode tools/diff-tests/test.jsafter touchingdiff.js, andnode tools/curl-tests/test.jsafter touchingcurl-parser.js.
JSON Keyper is free, with no accounts. If it saves you time, you can sponsor it on GitHub.
MIT - Ashish Singh.