All posts

JSONPath: A Practical Guide to Querying JSON

June 19, 2026 · DevTools

json
jsonpath
data
developer-tools

JSON is easy for machines and verbose for humans. When a response nests five levels deep and you need "every email address in every order," writing the loop by hand gets old fast. JSONPath is a small query language for pointing at the parts of a JSON document you care about.

You can try every example below in the JSONPath Evaluator — it runs in your browser.

The sample document

{
  "store": {
    "book": [
      { "category": "reference", "author": "Nigel Rees", "title": "Sayings", "price": 8.95 },
      { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword", "price": 12.99 },
      { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "price": 8.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

The root and dot notation

$ is the root object. Navigate with dots:

  • $.store.bicycle.color"red"
  • $.store.book[0].author"Nigel Rees"
  • $.store.book[1].title"Sword"

Bracket notation

Use brackets when keys contain odd characters or when you want an index:

  • $['store']['book'][2]['price']8.99
  • $.store.book[-1] → the last book (negative indexing, supported by most implementations)

Wildcards

* matches "everything at this level":

  • $.store.book[*].author → all authors: ["Nigel Rees", "Evelyn Waugh", "Herman Melville"]
  • $.store.* → the book array and the bicycle object

Recursive descent

.. means "search at any depth." This is the one people reach for most:

  • $..author → every author, anywhere in the tree
  • $..price → every price, including the bicycle's
  • $..book[0] → the first book found at any depth

Array slices

Slice arrays Python-style:

  • $.store.book[0:2] → first two books
  • $.store.book[1:] → everything from index 1 onward
  • $.store.book[::-1] → the books reversed

Filter expressions

Filters narrow results with a test in [?(...)]. Inside, @ refers to the current element:

  • $..book[?(@.price < 10)] → books cheaper than 10
  • $..book[?(@.category == 'fiction')] → only fiction
  • $..book[?(@.author =~ /melville/i)] → regex match on author

Two things that trip people up

  1. JSONPath has no single spec. RFC 9535 (JSONPath) standardized the basics recently, but older libraries (Stefan Goessner's original, Jayway, jsonpath-plus) differ on filters, regex, and functions. If an expression works in one tool and not another, this is usually why.
  2. Filters can't sort or aggregate. JSONPath selects; it doesn't compute sums or reorder. Sort the results in your own code.

When to reach for JSONPath

JSONPath shines for ad-hoc extraction — digging a value out of an unfamiliar API response, or pulling many matching values without writing traversal code. For heavy, repeated transforms you'll want JMESPath or a real query layer, but for "give me every price under 10," JSONPath is the right tool.

Paste a document and an expression into the JSONPath Evaluator, and tidy messy input first with the JSON Formatter.

Tools mentioned in this post