JSONPath in 5 minutes: query nested data without writing code
You have a JSON API response with 200 lines of nested objects and arrays. You need the price field from every item in the order.items array where quantity > 1. Writing a loop in code takes 10 lines. JSONPath does it in one expression: $.order.items[?(@.quantity > 1)].price.
The test payload
All examples in this post use this order payload. It has nested objects, arrays, mixed types, and a realistic structure you'd see from any ecommerce API.
{
"order": {
"id": "ORD-9921",
"status": "shipped",
"customer": {
"id": 4821,
"name": "Sarah Chen",
"tier": "premium"
},
"items": [
{
"sku": "PROD-441",
"name": "Wireless Keyboard",
"category": "electronics",
"quantity": 1,
"unit_price": 2499,
"in_stock": true
},
{
"sku": "PROD-229",
"name": "USB-C Hub",
"category": "electronics",
"quantity": 2,
"unit_price": 1299,
"in_stock": true
},
{
"sku": "PROD-881",
"name": "Desk Mat",
"category": "accessories",
"quantity": 1,
"unit_price": 799,
"in_stock": false
}
],
"shipping": {
"method": "express",
"address": {
"city": "Bangalore",
"pin": "560001",
"country": "IN"
},
"estimated_days": 2
},
"totals": {
"subtotal": 5896,
"discount": 0,
"tax": 1061.28,
"grand_total": 6957.28
}
}
}Basic syntax
Every JSONPath expression starts with $, which represents the root of the document.
| Syntax | Meaning |
|---|---|
$ | Root element |
.key | Child key (dot notation) |
['key'] | Child key (bracket notation — use for keys with spaces or special characters) |
[n] | Array index (0-based) |
[*] | All elements of an array or all values of an object |
.. | Recursive descent (search all levels) |
[start:end] | Array slice |
[?(...)] | Filter expression |
@ | Current element (inside filter expressions) |
Basic queries
Get the order ID:
$.order.idResult: "ORD-9921"
Get the customer name:
$.order.customer.nameResult: "Sarah Chen"
Get the shipping city:
$.order.shipping.address.cityResult: "Bangalore"
Get the first item (index 0):
$.order.items[0]Result: the Wireless Keyboard object.
Get all item names:
$.order.items[*].nameResult: ["Wireless Keyboard", "USB-C Hub", "Desk Mat"]
Array slicing
JSONPath uses Python-style slice notation. The end index is exclusive.
Get the last item:
$.order.items[-1]Result: the Desk Mat object.
Get the first two items:
$.order.items[0:2]Result: Wireless Keyboard and USB-C Hub (index 0 and 1; end index is exclusive).
Get every other item:
$.order.items[::2]Result: index 0 and 2 (Wireless Keyboard, Desk Mat).
jsonpath-plus (the library used in PDF Mavericks' JSON formatter) supports it fully. RFC 9535, published in 2024 as an IETF standard, now specifies slice selectors formally.Filter expressions
Filter expressions select elements that match a condition. They use the [?()] syntax with @ representing the current element.
Items where quantity is greater than 1:
$.order.items[?(@.quantity > 1)]Result: USB-C Hub object (quantity is 2).
Items in the electronics category:
$.order.items[?(@.category == "electronics")]Result: Wireless Keyboard and USB-C Hub.
Items that are in stock:
$.order.items[?(@.in_stock == true)]Result: Wireless Keyboard and USB-C Hub.
Items where unit_price is under 1000:
$.order.items[?(@.unit_price < 1000)]Result: Desk Mat (799).
Get just the names of items where quantity > 1:
$.order.items[?(@.quantity > 1)].nameResult: ["USB-C Hub"]
Combine conditions with && and ||:
Electronics items that are in stock:
$.order.items[?(@.category == "electronics" && @.in_stock == true)]Result: Wireless Keyboard and USB-C Hub.
Recursive descent
The .. operator searches at any depth in the document. Useful when you don't know exactly where a field lives.
Find all city values anywhere in the document:
$..cityResult: ["Bangalore"]
Find all unit prices at any level:
$..unit_priceResult: [2499, 1299, 799]
Find every id field at any level:
$..idResult: ["ORD-9921", 4821] — picks up both order.id and order.customer.id.
Get all values directly under totals:
$.order.totals.*Result: [5896, 0, 1061.28, 6957.28]
Common patterns in API work
Extract all SKUs from a paginated list response
If your API returns { "data": [...], "pagination": {...} }, use:
$.data[*].skuCheck if a specific field exists
$.order.items[?(@.discount)]Returns items that have a discount property. If none do, the result is empty.
Get a field from a slice of a nested array
$.data[10:20][*].nameSlice to items 10–19 first, then extract names.
JSONPath vs jq
JSONPath is widely supported in no-code tools, APIs, and testing frameworks. jq is more powerful — it's a full transformation language — but requires installation and a different syntax.
Use JSONPath when:
- Querying in Postman, Insomnia, AWS Step Functions, or k6
- You want to extract values without transformation
- You want something readable and standardized (RFC 9535)
- You're using a no-code or low-code tool
Use jq when:
- You need to transform the structure, not just extract
- You're in a shell script or CI pipeline
- You need arithmetic, string manipulation, or conditionals
- You're processing many files in batch
Run JSONPath Queries in Your Browser
Paste your JSON into our formatter, then use the JSONPath field to query it. Powered by jsonpath-plus — the same library used in Postman and Insomnia. Results update as you type. Your JSON stays in your tab.