Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Parsing JSON with JMESPath in Python takes two steps: decode the JSON text into ordinary Python dictionaries, lists, and scalar values with json.loads(), then evaluate a JMESPath expression against that data with jmespath.py. For example, jmespath.search('people[0].name', data) returns the first person’s name without a chain of manual dictionary and list lookups.
The basic workflow
JMESPath is a declarative query language for JSON-shaped data. The Python implementation, jmespath.py, evaluates an expression against data that has already been decoded. It does not replace JSON decoding: keep json.loads() for text and use JMESPath for selecting or transforming the resulting structure.
import json
import jmespath
json_text = '{"people": [{"name": "Mina", "active": true}]}'
data = json.loads(json_text)
name = jmespath.search('people[0].name', data)
print(name) # Mina
Install the package named jmespath in your project environment using your normal Python package workflow, then import it as jmespath. Keep the package and Python versions managed by your environment; the expression syntax described here comes from the JMESPath language specification.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchDecode JSON before querying it
JSON text
json.loads() accepts a Python string, bytes, or bytearray containing valid JSON and returns Python values: objects become dictionaries, arrays become lists, strings remain strings, numbers become numeric values, booleans become True or False, and JSON null becomes None.
#1 Best Overall
import json
payload = json.loads('{"account": {"id": 42, "enabled": true}, "tags": ["api", "python"]}')
print(type(payload)) # dict
print(payload['account']['id']) # 42
Files and responses
For a file, read its contents and pass the string to json.loads(), or use json.load(file_object). For an HTTP client that already exposes a JSON-decoding method, use that method and pass the resulting Python object to JMESPath. Do not pass the raw response object or an undecoded JSON string to jmespath.search().
import json
from pathlib import Path
text = Path('payload.json').read_text(encoding='utf-8')
data = json.loads(text)
result = jmespath.search('account.id', data)
If decoding fails, fix the JSON input first. A malformed document is a json.JSONDecodeError problem, not a JMESPath expression problem.
Core JMESPath expressions
| Goal | Expression | Result |
|---|---|---|
| Read a top-level key | name |
The value of name |
| Read a nested key | person.name |
The nested name |
| Read an array item | people[0].name |
The first item’s name; indexes are zero-based |
| Project one key from every item | people[*].name |
A list of names |
| Filter an array | people[?active == `true`] |
Items whose active value is true |
| Build a named result | {first: people[0].name, count: length(people)} |
A new object with two fields |
Use backticks around JSON literals such as `true`, `10`, and `null` in expressions. A quoted string literal is written with single or double quotes according to the expression grammar; when embedding an expression inside a Python string, choose outer quotes that keep the expression readable or escape the inner ones.
Project and filter collections
Projections
A projection applies the rest of an expression to each item in an array. Given this data:
data = {
'people': [
{'name': 'Mina', 'role': 'admin'},
{'name': 'Jules', 'role': 'editor'},
]
}
print(jmespath.search('people[*].name', data))
# ['Mina', 'Jules']
Projection behavior matters when a field is absent. A projected value that resolves to null can be omitted from the projected list, so test expressions against representative input rather than assuming the output has one position for every input object.
Rank #2
Filters
Filters select array elements whose condition is true. The expression below keeps only administrators:
admins = jmespath.search("people[?role == 'admin']", data)
print(admins)
# [{'name': 'Mina', 'role': 'admin'}]
Filters can compare fields, numbers, strings, and function results. Make sure the data type matches the comparison. Comparing a number to a quoted string is not the same as comparing two numbers.
Nested projections
Combine projections and nested access for API responses that contain several levels:
data = {
'teams': [
{'name': 'Core', 'members': [{'name': 'Mina'}, {'name': 'Jules'}]},
{'name': 'Data', 'members': [{'name': 'Ravi'}]},
]
}
team_names = jmespath.search('teams[*].name', data)
member_names = jmespath.search('teams[*].members[*].name', data)
print(team_names) # ['Core', 'Data']
print(member_names) # [['Mina', 'Jules'], ['Ravi']]
Shape the result with multi-selects and pipes
Multi-select hashes
A multi-select hash creates an object whose keys are the labels you provide. This is useful when downstream code needs a stable, smaller schema:
expression = '{id: account.id, email: account.profile.email, labels: tags}'
small = jmespath.search(expression, {
'account': {'id': 42, 'profile': {'email': '[email protected]'}},
'tags': ['api', 'python']
})
print(small)
# {'id': 42, 'email': '[email protected]', 'labels': ['api', 'python']}
Pipes
A pipe passes one expression’s result into another expression. For example, people[*] | [?active == `true`] | [].name first projects the people list, filters it, then extracts names. Pipes make long queries easier to reason about because each stage has a clear input and output shape.
Functions and type-aware expressions
JMESPath includes built-in functions for operations such as measuring a collection, sorting values, converting types, and inspecting a value. Function signatures are typed: the function name, argument count, and argument types must match what the language defines.
data = {'values': ['10', '20', '30']}
count = jmespath.search('length(values)', data)
number = jmespath.search('to_number(values[0])', data)
value_type = jmespath.search('type(values)', data)
print(count) # 3
print(number) # 10
print(value_type) # array
to_number is an explicit conversion, not a substitute for validating incoming data. A non-numeric string may produce an invalid-value evaluation error, depending on the implementation’s error handling. Use type(@) or a narrower expression while investigating an unexpected payload.
Missing keys, nulls, and evaluation errors
An unknown identifier evaluates to null according to the specification. In Python, that normally appears as None:
data = {'user': {'name': 'Mina'}}
print(jmespath.search('user.email', data)) # None
print(jmespath.search('user.email || `unknown`', data)) # unknown
Treat a null result as a possible missing field, not automatically as a failure. If null is a valid business value, distinguish it from an absent key in your application logic.
Evaluation errors are different. The specification defines error classes including invalid-type, invalid-value, unknown-function, and invalid-arity. A function receiving an array when it requires a number, an unknown function name, or the wrong number of arguments can raise an exception in Python. Catch exceptions at a boundary where you can log the expression and input shape, then fix the query or normalize the data.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →try:
result = jmespath.search('length(account.id)', data)
except Exception as exc:
raise ValueError('JMESPath evaluation failed') from exc
Keep the original exception available during debugging; broad catching is appropriate only when your application converts query failures into a controlled response.
Reusable and efficient queries
Compile an expression once
For a query used repeatedly, compile it and reuse the parsed expression:
expression = jmespath.compile('orders[?status == `open`].id')
for payload in payloads:
open_order_ids = expression.search(payload)
process(open_order_ids)
This keeps the expression in one place and avoids reparsing it in your own loop. It does not change the semantics of the query.
Keep expressions understandable
- Start with a field lookup, then add one projection or filter at a time.
- Use a multi-select hash when the consumer needs named fields rather than a large source object.
- Prefer explicit conversions and type checks at data boundaries.
- Log the expression and a redacted sample of the input when diagnosing production failures.
No comparative benchmark is established here, so do not assume JMESPath is faster than hand-written Python traversal. Its principal benefit is a portable, declarative expression for extraction and transformation; ordinary Python remains clearer for application-specific branching, side effects, and complex validation.
Free tools Windows power users keep installed
One-click scans. No signup required.
A practical debugging checklist
- Confirm decoding: print or inspect
type(data)and verify that the root is a dictionary or list, not a JSON string. - Check the path: evaluate each segment separately, such as
people, thenpeople[0], thenpeople[0].name. - Check array indexes: indexes start at zero, and an out-of-range lookup produces a null-like result.
- Check the shape before projecting:
items[*].namerequiresitemsto be an array. - Check literal types: use backtick JSON literals for booleans, numbers, and null; do not compare a numeric field with a quoted numeric string unless that is intentional.
- Check function arguments: verify both arity and input type when an expression raises an evaluation error.
- Reduce the query: remove filters, pipes, and functions until the smallest working expression is found, then add stages back one at a time.
JMESPath versus ordinary Python traversal
Use JMESPath when the task is primarily declarative selection: picking nested fields, filtering arrays, projecting values, or producing a smaller object with a repeatable expression. Use normal Python when the operation needs side effects, stateful logic, custom error recovery, or rules that are easier to read as statements.
Best Value
You can combine both approaches. Decode and validate a payload in Python, use JMESPath to select the relevant records, then apply domain-specific Python logic to those records. This keeps the query focused and leaves business rules in regular, testable code.
Or skip the browser setup
If your workflow also needs a clean visual capture of the webpage that produced or documents an API response, ScreenshotNeo can return a screenshot or PDF from one request. It is separate from JSON parsing: use JMESPath for data and ScreenshotNeo for page captures.
ScreenshotNeo removes cookie-consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
See the ScreenshotNeo API documentation for all options. A direct call looks like this:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
And in Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 screenshots each month with no card required; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Frequently Asked Questions
Can JMESPath modify my original Python dictionary?
No. A search evaluates an expression and returns a result; it does not provide in-place updates to the source object. Assign the returned value or perform mutations separately in Python.
What does a JMESPath search return when the input root is a list?
It can query a list directly. Use an index, projection, filter, or function appropriate to an array root, such as [?enabled == `true`] or [0].
Is JMESPath output always a Python dictionary?
No. The result can be a string, number, boolean, list, dictionary, or None, depending on the expression and input.
How can I make a query safe for changing API schemas?
Keep expressions small, treat missing fields as possible null results, validate critical fields in Python, and add tests for absent keys, empty arrays, and incorrect types.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

