Query a JSON document with JSONPath and see what comes back, live. Use it when the value you need is six levels down in an API response and you want the expression right before it goes into code.
$.store.book[?(@.price < 15)].title [
"The Left Hand of Darkness",
"Sapiens"
] Start every field in a filter with @. Inside a filter, @ means the current element, so write [?(@.price < 100)], never [?(price < 100)]. Without it, the filter returns nothing.
JSONPath selects; it cannot reshape. To rename keys, compute a total or build a new object, use jq instead, because JSONPath has no syntax for any of that.
JSONPath started as a blog post in 2007 and did not get a formal
specification until RFC 9535 in 2024. In the seventeen years
between, every library made its own decisions about filters,
unions, and what a missing key should do. This page uses
jsonpath-plus, which is permissive; the Jayway implementation
behind most Java tooling and the one built into Kubernetes
kubectl both differ in places.
For a simple path, the differences do not show. For anything
with a filter or a script expression, test against
the implementation you ship. A path that
works here can return a different result in Java or
kubectl.
# jq is the usual tool at a shell, and its syntax is its own
curl -s https://api.example.com/store | jq '.store.book[] | select(.price < 100000) | .title'
# jq can reshape, which JSONPath cannot
jq '{titles: [.store.book[].title], total: ([.store.book[].price] | add)}' store.json
# kubectl speaks JSONPath specifically
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
kubectl get nodes -o jsonpath='{.items[?(@.spec.unschedulable)].metadata.name}' If you are picking one to learn, we recommend jq. Learn JSONPath because kubectl, Kibana and much of the Java tooling use it.
// npm i jsonpath-plus
const { JSONPath } = require("jsonpath-plus");
JSONPath({ path: "$..book[?(@.price < 100000)].title", json: data });
// Get the paths rather than the values, which is handy for building
// an editable form or reporting where something was found
JSONPath({ path: "$..price", json: data, resultType: "path" });
// Both at once
JSONPath({ path: "$..price", json: data, resultType: "all" }); # pip install jsonpath-ng
from jsonpath_ng.ext import parse # .ext adds filter support
expr = parse("$..book[?price < 100000].title")
print([m.value for m in expr.find(data)])
# Note the syntax difference: jsonpath-ng writes filters without the @
# and without the parentheses that jsonpath-plus requires. Expressions
# are not portable between libraries. JSONPath began as a 2007 blog post by Stefan Goessner, modeled on XPath, and spread through implementations that each filled the gaps differently. RFC 9535 standardized it in 2024. However, the RFC does not change the libraries already in your dependency tree, so test any expression beyond a plain path against the implementation you ship.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
A query language for JSON, loosely modeled on XPath. $ is the root, dots walk into keys, brackets index arrays, and .. searches at any depth. Use it when the value you want is six levels down in an API response.
Child access ($.store.book), wildcards ($.book[*]), recursive descent ($..price), slices ([0:2]), unions ([0,2]) and filters ([?(@.price < 10)]).
No. jq is a full transformation language with its own syntax; JSONPath only selects. To reshape the output rather than extract from it, use jq.
Check for a missing @ first. Inside a filter, @ is the current item, so write [?(@.price < 10)], not [?(price < 10)]. String comparisons also need quotes: [?(@.role == 'admin')].
No. JSONPath had no formal specification until RFC 9535 in 2024, so libraries differ on filters, unions and how they treat a missing key. Test any expression beyond a plain path against the library you ship.
To read the document first, the JSON formatter lays it out and the JSON table viewer turns an array of objects into rows. For matching inside strings rather than selecting nodes, use the regex tester.