jq vs JSONPath: Which Should You Use?
· 4 min read
Use JSONPath when you only need to select values and want a small, standardized syntax that works inside applications and config files; use jq when you need to transform, reshape, aggregate or script JSON, especially on the command line. JSONPath answers "which parts?", while jq is a full programming language for JSON.
At a glance
| JSONPath | jq | |
|---|---|---|
| What it is | Selection language | Functional programming language for JSON |
| Standard | IETF RFC 9535 (2024) | Defined by the jq manual and reference implementation (gojq and jaq are compatible reimplementations) |
| Output | List of matching values | Any JSON you construct |
| Filtering | Yes, [?@.x > 1] | Yes, select(.x > 1) |
| Build new objects | No | Yes, {name: .title} |
| Aggregation (sum, group) | No | Yes, add, group_by, reduce |
| Variables, functions | A few built-in functions | Variables, user-defined functions, modules |
| Typical home | Libraries in app code, API testing tools, Kubernetes kubectl -o jsonpath, AWS Step Functions | Shell scripts, CI pipelines, ad-hoc terminal work |
The same queries side by side
These use a document shaped like {"orders": [{"id": 1, "total": 25, "status": "paid", "customer": {"email": "a@example.com"}}, ...]}.
| Task | JSONPath | jq |
|---|---|---|
| All orders | $.orders[*] | .orders[] |
| First order | $.orders[0] | .orders[0] |
| Every total | $.orders[*].total | .orders[].total |
Every id at any depth | $..id | .. | .id? // empty |
| Paid orders | $.orders[?@.status == 'paid'] | .orders[] | select(.status == "paid") |
| Orders over 100 | $.orders[?@.total > 100] | .orders[] | select(.total > 100) |
| Customer emails | $.orders[*].customer.email | .orders[].customer.email |
| Number of orders | Not in the standard (count the matches in code) | .orders | length |
| Sum of totals | Not possible | [.orders[].total] | add |
| Reshape each order | Not possible | .orders[] | {id, email: .customer.email} |
For simple paths the syntax is nearly identical: drop the $, use [] instead of [*]. The gap opens as soon as you want output that is not a subset of the input.
What only jq can do
# Group orders by status and total them
jq '.orders | group_by(.status) | map({status: .[0].status, revenue: (map(.total) | add)})' orders.json
# Turn an array into a lookup object keyed by id
jq '.orders | map({key: (.id | tostring), value: .}) | from_entries' orders.json
# Emit CSV rows
jq -r '.orders[] | [.id, .status, .total] | @csv' orders.json
# Update a value in place and write a new file
jq '.orders |= map(if .status == "pending" then .status = "cancelled" else . end)' orders.json > out.jsonjq also reads streams of JSON values (NDJSON) natively, can combine multiple files with --slurp, and accepts arguments with --arg and --argjson so scripts never need string interpolation into the query.
Why JSONPath still matters
- It is embedded everywhere. API testing tools, Kubernetes, cloud workflow services and many databases accept JSONPath (or a close dialect) in configuration. You cannot drop a jq program into those fields.
- It is safe to accept from users. A selection-only language with no loops or user-defined functions is easier to sandbox and reason about than a Turing-complete one.
- It is standardized. RFC 9535 gives one specification and a compliance test suite, so behavior is converging across libraries.
- Libraries in every language. JSONPath implementations exist for JavaScript, Python, Java, Go, .NET and more. jq is available as a C library and several reimplementations, but it is most often used as a CLI.
Decision guide
| Situation | Use |
|---|---|
| One-off exploration in a terminal | jq |
| Shell script or CI step that reshapes JSON | jq |
| A field in a config file asks for a path | JSONPath (check the exact dialect the tool documents) |
| App code that lets users pick fields | JSONPath |
| Asserting values in API tests | JSONPath, or your test framework's native syntax |
| Aggregations, joins, format conversion | jq, or a real programming language |
Try both in the browser
Our JSON formatter has a query bar that detects whether you typed JSONPath (starting with $) or a jq-style filter and runs it live on your document, locally in your browser. For JSONPath specifically, the JSONPath tester shows every match with its path. The JSONPath cheat sheet covers the full syntax, and if your documents are huge, see how to search huge JSON files for jq's streaming mode.
FAQ
Is jq better than JSONPath?
jq is more powerful because it can transform and aggregate data. JSONPath is simpler, standardized and easier to embed in apps and configs. Pick by task, not by power.
Can jq use JSONPath syntax?
No. jq has its own syntax. Simple paths look similar (.a.b[0] vs $.a.b[0]), but filters and wildcards are written differently.
What does kubectl -o jsonpath use?
kubectl uses its own JSONPath template dialect with {} delimiters and extra features such as range, for example kubectl get pods -o jsonpath='{.items[*].metadata.name}'.
How do I sum values with JSONPath?
You cannot in standard JSONPath; it only selects values. Select them with JSONPath and sum in code, or use jq: [.items[].price] | add.
Is there an online jq playground that does not upload my JSON?
Our JSON formatter runs jq-style queries in your browser without sending the document anywhere.

