Ultimate Guide

The Complete Guide to JSON Path (2026)

JSON Path is the shortest distance between a JSON document and the value you actually want. This guide covers everything a working developer needs — syntax, dot vs bracket notation, the RFC 9535 standard, JSONPath vs jq, and real code for JavaScript, Python, Postman, and Cypress. No fluff. Just the parts you'll use.

Updated July 2026 ~18 min read By the JSON Path Finder team

1. What is a JSON path?

A JSON path is a string expression that points to a specific value inside a JSON document. Think of it as an address: the root of the document is $, and each step deeper is either a key name (.user) or an array index ([0]).

Given this response from an API:

{
  "user": {
    "id": 42,
    "name": "Ada Lovelace",
    "emails": ["ada@example.com", "ada.l@work.io"],
    "address": {
      "city": "London",
      "postcode": "NW1 6XE"
    }
  }
}

…the path to Ada's first email is $.user.emails[0], and the path to her city is $.user.address.city. That's the whole idea. Everything else in this guide is variations, edge cases, and language-specific glue.

RFC 9535 formalized JSONPath in early 2024. Before that, every library implemented a slightly different dialect. Most modern libraries now align with the RFC, but filter expressions and script blocks still have quirks — we'll flag them where they matter.

2. Dot vs bracket notation

JSONPath supports two ways to walk into an object. They're interchangeable most of the time, but each has situations where it's the only correct choice.

Dot notation (readable, but limited)

$.user.address.city         // "London"
$.user.emails[0]            // "ada@example.com"
$.user.id                   // 42

Dot notation only works for keys that are valid identifiers: letters, digits, underscores. If a key has a dash, a space, a dot, or starts with a number, dot notation breaks.

Bracket notation (verbose, but universal)

$['user']['address']['city']
$['user']['emails'][0]
$['x-request-id']              // dash key → brackets required
$['first name']                // space in key → brackets required
$['user'][0]                   // numeric key → brackets required

Bracket notation always works. It's slightly noisier, but if you're generating paths programmatically — say, from API responses you don't fully control — brackets are the safer default.

Key looks like… Use Example
userNameEither$.userName or $['userName']
user-nameBracket$['user-name']
first nameBracket$['first name']
Array elementBracket$.items[3]
WildcardEither$.items[*] or $.items.*

3. JSONPath expressions cheat sheet

These are the operators you'll actually use. Bookmark this section.

Operator Meaning Example
$Root of the document$.user
.Child by name (dot notation)$.user.name
[...]Child by name or index (bracket notation)$['user'][0]
*Wildcard — all children$.users[*].email
..Recursive descent — any depth$..email
[start:end]Array slice$.items[0:3]
[?(...)]Filter expression$.users[?(@.age > 18)]
@The current node in a filter$.orders[?(@.total > 100)]

Filter expressions in one example

{
  "orders": [
    { "id": 1, "total": 42,  "status": "paid" },
    { "id": 2, "total": 199, "status": "paid" },
    { "id": 3, "total": 15,  "status": "refunded" }
  ]
}
$.orders[?(@.status == 'paid' && @.total > 50)]
// → [ { "id": 2, "total": 199, "status": "paid" } ]

Filters are where JSONPath dialects diverge the most. Some libraries require single quotes, some require double. Some support && / ||, some only and / or. When in doubt, check your library's readme before writing 30 filters and wondering why nothing works.

4. Try it live: JSON Path Finder

Reading about paths only gets you so far. The fastest way to build intuition is to paste your own JSON and click around. That's exactly what JSON Path Finder does — click any value, get the exact path.

Open the tool in a new tab

No signup. No install. Handles large JSON without freezing your browser.

{
  "user": {
    "name": "Ada",  ← click
    "emails": [...]
  }
}

Path: $.user.name ✓ copied

5. JSONPath vs JSONPointer vs jq

People conflate these three constantly. They solve overlapping problems, but they're not the same tool.

Tool Purpose Sample syntax Best when…
JSONPath Locate values in JSON $.users[?(@.age>18)].name You're inside code, tests, or a browser
JSONPointer Point at a single node (RFC 6901) /users/0/name You need an unambiguous, standard reference
jq Transform and filter JSON on the CLI .users[] | select(.age>18) | .name You're in a terminal, piping between commands

Rough rule of thumb: JSONPath reads data, jq reshapes it, JSONPointer addresses it. If you find yourself building elaborate output structures, you're probably reaching for jq territory.

6. Using JSON paths in JavaScript

JavaScript has three tiers of "getting a value out of nested JSON," from native to full JSONPath library.

Tier 1 — Native optional chaining

const city = data?.user?.address?.city;
const firstEmail = data?.user?.emails?.[0];

Good enough for known, static paths. Zero dependencies. Breaks down when paths are dynamic strings.

Tier 2 — Lodash get()

import _ from 'lodash';

_.get(data, 'user.address.city');
_.get(data, ['user', 'emails', 0]);
_.get(data, 'user.address.zip', 'N/A'); // default value

Handy when the path itself is a string variable — for example, coming from a config file or a URL parameter.

Tier 3 — jsonpath-plus (real JSONPath)

import { JSONPath } from 'jsonpath-plus';

// All email addresses, no matter how deep
const emails = JSONPath({ path: '$..email', json: data });

// Every paid order with a total over $50
const bigPaidOrders = JSONPath({
  path: "$.orders[?(@.status=='paid' && @.total>50)]",
  json: data
});

Use jsonpath-plus the moment you need wildcards, recursive descent, or filters. It's small, maintained, and matches the RFC dialect closely.

7. Using JSON paths in Python

Python has a native dict-first approach, plus two mature JSONPath libraries. Pick based on what you need.

Native dict access

city = data["user"]["address"]["city"]
first_email = data["user"]["emails"][0]

# Safe version
city = data.get("user", {}).get("address", {}).get("city")

jsonpath-ng (recommended)

from jsonpath_ng.ext import parse

expr = parse("$.orders[?(@.status=='paid' & @.total>50)]")
matches = [m.value for m in expr.find(data)]

jsonpath-ng.ext gives you filter expressions and arithmetic. The plain jsonpath-ng module is stricter but faster on simple paths. For most projects, use .ext.

Piping into pandas

import pandas as pd
from jsonpath_ng.ext import parse

users = [m.value for m in parse("$.users[*]").find(data)]
df = pd.DataFrame(users)

Common pattern for data engineers: extract a "row-like" slice with JSONPath, then hand it to pandas.

8. Using JSON paths in Postman

Postman ships with Chai assertions and access to the response as a JavaScript object. Most Postman tests don't actually need a JSONPath library — you can walk the tree directly.

pm.test("User has valid email", function () {
  const body = pm.response.json();
  pm.expect(body.user.emails[0]).to.include('@');
});

When the response is deeply nested or paths are dynamic, add jsonpath-plus via the Postman sandbox:

const { JSONPath } = require('jsonpath-plus');
const body = pm.response.json();

const paidOrderIds = JSONPath({
  path: "$.orders[?(@.status=='paid')].id",
  json: body
});

pm.test("All paid orders returned", () => {
  pm.expect(paidOrderIds.length).to.be.above(0);
});

9. Using JSON paths in Cypress & Playwright

Cypress

cy.request('/api/user/42')
  .its('body.user.address.city')
  .should('eq', 'London');

// For arrays / filters, drop into a callback
cy.request('/api/orders').then(({ body }) => {
  const paid = body.orders.filter(o => o.status === 'paid');
  expect(paid.length).to.be.greaterThan(0);
});

Playwright

const response = await request.get('/api/user/42');
const body = await response.json();

expect(body.user.address.city).toBe('London');
expect(body).toMatchObject({
  user: { emails: expect.arrayContaining([expect.stringContaining('@')]) }
});

Both tools let you use plain JS access. Reach for JSONPath only when paths are dynamic or filters get gnarly.

10. Common mistakes & debugging

❌ Dot notation on a key with a dash

$.x-request-id is parsed as $.x minus request minus id. Use $['x-request-id'].

❌ Forgetting @ inside filters

Inside [?(...)], the current item is @, not $. Writing [?($.status == 'paid')] refers to the root's .status, not the item's.

❌ Assuming all libraries agree

$..book[(@.length-1)] works in some libraries and errors in others. If a path works in one language but not another, the dialect is the difference — not your JSON.

❌ Silent empty results

A path that matches nothing returns [], not an error. In tests, always assert the length is what you expect before asserting on values.

The fastest way to debug a bad path is to paste the JSON into the tool and click the value you actually want. If the click path differs from what you wrote, you have your answer.

Frequently Asked Questions

What is a JSON path?

A JSON path is a string expression that identifies the location of a value inside a JSON document — similar to how an XPath identifies a node in XML. It uses a root symbol ($) followed by dot or bracket notation to walk from the root of the document down to a specific value.

What is the difference between JSONPath and jq?

JSONPath is a query syntax for locating values inside JSON. jq is a command-line JSON processor with its own richer language that can transform, filter, and reshape JSON. JSONPath is simpler and works well inside code and API tests; jq is more powerful for terminal workflows.

When should I use dot notation vs bracket notation?

Use dot notation for standard alphanumeric keys ($.user.name). Use bracket notation when a key contains spaces, dashes, reserved characters, or when you need to access an array element by index ($['user-name'], $.items[0]).

Is JSONPath a formal standard?

JSONPath was originally proposed by Stefan Gössner in 2007. In 2024 it was formalized as RFC 9535. However, many libraries still implement small dialect differences, so always check your library's docs for edge cases like filter expressions or script expressions.

Can I use JSONPath in Postman and Cypress?

Yes. Postman supports JSONPath through pm.expect(pm.response.json()) with libraries like jsonpath-plus. Cypress commonly uses .its() with dot notation, but you can also load a JSONPath library for more advanced assertions. Playwright works the same way.