JSONPath Cheatsheet for Debugging APIs
JSONPath is a query language for JSON, similar to XPath for XML. It allows you to extract specific data from complex JSON structures using concise path expressions. This is especially useful when debugging API responses, processing large datasets, or extracting values from nested objects.
This cheatsheet covers the essential JSONPath operators with practical examples focused on API debugging scenarios.
Quick Reference
| Operator | Description | Example |
|---|---|---|
$ | Root object | $.user |
@ | Current object (in filters) | @.price |
. | Child operator | $.user.name |
.. | Recursive descent | $..email |
* | Wildcard (all elements) | $.users[*].name |
[] | Array subscript | $.items[0] |
[start:end] | Array slice | $.items[0:3] |
[?()] | Filter expression | $.items[?(@.price < 10)] |
Sample Data
We'll use this API response structure for examples:
{
"status": "success",
"user": {
"id": 101,
"name": "Alice Johnson",
"email": "alice@example.com",
"roles": ["admin", "editor"]
},
"products": [
{
"id": 1,
"name": "Laptop",
"price": 999,
"inStock": true,
"tags": ["electronics", "computers"]
},
{
"id": 2,
"name": "Mouse",
"price": 25,
"inStock": true,
"tags": ["electronics", "accessories"]
},
{
"id": 3,
"name": "Monitor",
"price": 349,
"inStock": false,
"tags": ["electronics", "displays"]
}
],
"metadata": {
"timestamp": "2026-01-11T10:00:00Z",
"version": "1.0",
"pagination": {
"page": 1,
"pageSize": 10,
"total": 45
}
}
}Basic Selection
Root and Direct Access
| JSONPath | Result |
|---|---|
$ | Entire document |
$.status | "success" |
$.user.name | "Alice Johnson" |
$.user.email | "alice@example.com" |
Nested Object Access
| JSONPath | Result |
|---|---|
$.metadata.version | "1.0" |
$.metadata.pagination.page | 1 |
$.metadata.pagination.total | 45 |
Array Operations
Array Indexing
| JSONPath | Result |
|---|---|
$.products[0] | First product (Laptop) |
$.products[1].name | "Mouse" |
$.products[-1] | Last product (Monitor) |
$.user.roles[0] | "admin" |
Array Slicing
| JSONPath | Result |
|---|---|
$.products[0:2] | First two products |
$.products[1:] | All products except first |
$.products[:2] | First two products |
$.products[-2:] | Last two products |
Wildcards
| JSONPath | Result |
|---|---|
$.products[*].name | All product names |
$.products[*].price | All product prices: [999, 25, 349] |
$.products[*].id | All product IDs: [1, 2, 3] |
Deep Scanning (Recursive Descent)
The .. operator searches the entire document tree:
| JSONPath | Result |
|---|---|
$..name | All "name" fields at any level |
$..price | All "price" fields: [999, 25, 349] |
$..id | All "id" fields: [101, 1, 2, 3] |
$..tags[0] | First tag of all products |
Filter Expressions
Filter expressions use [?()] syntax with @ referring to the current item:
Comparison Operators
| JSONPath | Result |
|---|---|
$.products[?(@.price < 100)] | Products under $100 (Mouse) |
$.products[?(@.price >= 300)] | Products $300+ (Laptop, Monitor) |
$.products[?(@.inStock == true)] | In-stock products (Laptop, Mouse) |
$.products[?(@.inStock != true)] | Out-of-stock products (Monitor) |
Logical Operators
| JSONPath | Result |
|---|---|
$.products[?(@.price < 100 && @.inStock)] | Cheap + in stock (Mouse) |
$.products[?(@.price > 500 || !@.inStock)] | Expensive or out of stock |
Existence Checks
| JSONPath | Result |
|---|---|
$.products[?(@.tags)] | Products with tags field |
$..products[?(@.discount)] | Products with discount field (none) |
Real-World API Debugging Scenarios
Scenario 1: Extract All Error Messages
{
"results": [
{"status": "success", "data": {...}},
{"status": "error", "message": "Invalid token"},
{"status": "success", "data": {...}},
{"status": "error", "message": "Database timeout"}
]
}
// Extract all error messages
$.results[?(@.status == 'error')].message
// Result: ["Invalid token", "Database timeout"]Scenario 2: Find Items with Specific Tags
// Find all products tagged "electronics"
$.products[?(@.tags[*] == 'electronics')]
// Find products with BOTH "electronics" AND "computers" tags
$.products[?(@.tags[*] == 'electronics' && @.tags[*] == 'computers')]Scenario 3: Extract Nested Pagination Data
{
"response": {
"data": [...],
"meta": {
"pagination": {
"current_page": 2,
"total_pages": 10,
"next_page": 3
}
}
}
}
// Extract pagination info
$.response.meta.pagination
// or use deep scan
$..paginationScenario 4: Filter Multi-Level Data
{
"users": [
{
"name": "Alice",
"orders": [
{"id": 1, "total": 100, "paid": true},
{"id": 2, "total": 50, "paid": false}
]
},
{
"name": "Bob",
"orders": [
{"id": 3, "total": 200, "paid": true}
]
}
]
}
// Get all unpaid orders (any user)
$..orders[?(@.paid == false)]
// Get users with unpaid orders
$.users[?(@.orders[?(@.paid == false)])]Scenario 5: Extract IDs for Bulk Operations
// Get IDs of all in-stock products under $100
$.products[?(@.inStock && @.price < 100)].id
// Get IDs of all products
$.products[*].id
// Get IDs using deep scan (works even if structure changes)
$..products[*].idAdvanced Patterns
Combining Multiple Filters
// Products: in stock, under $500, with "electronics" tag
$.products[?(
@.inStock == true &&
@.price < 500 &&
@.tags[*] == 'electronics'
)]Regex Matching (Implementation-Dependent)
// Products where name starts with "M"
$.products[?(@.name =~ /^M/)]
// Users with .com email addresses
$.users[?(@.email =~ /\.com$/)]Note: Regex support varies by JSONPath implementation.
Script Expressions (Advanced)
// Products with price within 10% of average
$.products[?(@.price < 400)]
// Calculate length
$.products[?(@.tags.length > 1)]Common Pitfalls
Pitfall 1: Forgetting the Root
// Wrong - no root
products[0].name
// Correct
$.products[0].namePitfall 2: Confusing . and ..
// Single dot - direct child
$.user.name // Only gets user.name
// Double dot - recursive search
$..name // Gets ALL "name" fields at any levelPitfall 3: Wildcard vs Filter
// Wildcard - gets all elements
$.products[*].name
// Filter - gets elements matching condition
$.products[?(@.inStock)].namePitfall 4: Array Index Out of Bounds
// If array has 3 items:
$.products[10] // Returns undefined/null, not error
// Use filters or wildcards for safety
$.products[?(@.id == 10)]Implementation Differences
JSONPath implementations vary across languages and libraries:
JavaScript (jsonpath library)
const jp = require('jsonpath');
const result = jp.query(data, '$.products[?(@.price < 100)]');
console.log(result);Python (jsonpath-ng)
from jsonpath_ng import parse
expression = parse('$.products[*].name')
matches = [match.value for match in expression.find(data)]Go (gjson)
import "github.com/tidwall/gjson"
result := gjson.Get(jsonString, "products.#(price<100).name")
fmt.Println(result.String())Testing JSONPath Expressions
Use the DevToys Pro JSONPath Tester to:
- Test expressions against real API responses
- Debug complex filter conditions
- Validate path syntax before implementing in code
- Experiment with different operators and combinations
- Compare results across different query approaches
Best Practices
1. Start Simple, Then Refine
// Step 1: Get all products
$.products
// Step 2: Get all product names
$.products[*].name
// Step 3: Filter by condition
$.products[?(@.inStock)].name2. Use Filters Over Complex Indexing
// Less maintainable - assumes order
$.products[2].name
// More maintainable - finds by property
$.products[?(@.id == 3)].name3. Deep Scan Sparingly
// Can be slow on large documents
$..price
// Prefer specific paths when possible
$.products[*].price4. Handle Missing Data
// Check existence before accessing
$.user.profile?.address?.city
// Or use existence filter
$.users[?(@.profile && @.profile.address)]Conclusion
JSONPath provides a powerful, concise syntax for querying JSON structures. Key takeaways:
- Use
$for root,.for child access,..for recursive search - Array operations support indexing, slicing, and wildcards (
[0],[0:3],[*]) - Filter expressions
[?()]enable conditional selection - Combine operators for complex queries:
$.products[?(@.price < 100)].name - Test expressions before deploying to production
- Be aware of implementation differences across languages
For testing and debugging JSONPath expressions, use the JSONPath Tester to validate queries against real API responses and experiment with different selection strategies.