JSON Tools
Tutorial

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.

May 1, 2026
7 min read
PDF Mavericks Team

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.

SyntaxMeaning
$Root element
.keyChild 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.id

Result: "ORD-9921"

Get the customer name:

$.order.customer.name

Result: "Sarah Chen"

Get the shipping city:

$.order.shipping.address.city

Result: "Bangalore"

Get the first item (index 0):

$.order.items[0]

Result: the Wireless Keyboard object.

Get all item names:

$.order.items[*].name

Result: ["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).

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)].name

Result: ["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:

$..city

Result: ["Bangalore"]

Find all unit prices at any level:

$..unit_price

Result: [2499, 1299, 799]

Find every id field at any level:

$..id

Result: ["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[*].sku

Check 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][*].name

Slice 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.