jmespath review
jmespath 1.1.0 evaluates a standardized query language against JSON-shaped Python values. `search()` can select nested fields, filter and slice arrays, flatten projections, call built-in functions, and construct new lists or mappings without changing the input. `compile()` parses an expression once for repeated use. The 1.1.0 release fixes a parser-cache concurrency bug, supports Python 3.12 through 3.14, drops Python 3.7 and 3.8, and now requires Python 3.9 or newer.
jmespath 1.1.0 installed in 0.2 seconds as 1 dependency-free package using 1 MB, with 0 audit findings in our sandbox; it fits portable read-only queries over JSON-shaped data. Keep fixed application logic in Python, and choose JSONPath when recursive descent is a hard requirement.
We installed it
| Install | ✓ · 0.2s | 1 package on disk · 1 MB |
| Import | ✓ | import jmespath in 0.12s · pure Python · requires Python >=3.9 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does jmespath install cleanly?
Yes. In a fresh container with an empty cache, pip install jmespath finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does jmespath need to run?
Python >=3.9, and nothing compiled: it is pure Python. In our run import jmespath succeeded in 0.12s.
jmespath or jsonpath-ng: which should you use?
jsonpath-ng: Choose it for JSONPath syntax, recursive descent, and matches that retain paths into the document. jmespath 1.1.0 installed in 0.2 seconds as 1 dependency-free package using 1 MB, with 0 audit findings in our sandbox; it fits portable read-only queries over JSON-shaped data.
When should you not use jmespath?
The query needs JSONPath recursive descent to locate a field at any depth; JMESPath has no equivalent .. operator.
Discussed on
Use it if
- Users or configuration files need a constrained, read-only query language over dictionaries and lists.
- One expression should work in Python, the AWS CLI `--query` flag, and Boto3 paginator searches.
- The program repeatedly applies a precompiled field-selection or filtering expression to many documents.
- Cross-language behavior matters enough to prefer an ABNF grammar and shared compliance tests over a homegrown selector.
- The query needs JSONPath recursive descent to locate a field at any depth; JMESPath has no equivalent `..` operator.
- The operation must update the source document. JMESPath only reads and constructs result values.
- All expressions are fixed in source and a Python comprehension would state the data shape more plainly.
- Static typing must determine the result type. A runtime expression may produce a scalar, list, mapping, or None based on both query and input.
- Business logic depends on custom functions. The Python README labels that extension API experimental and subject to change.
Setup reality
We installed jmespath 1.1.0 in a fresh Python 3.12 Bookworm container in 0.2 seconds. It added 1 package, occupied 1 MB, and pip-audit reported 0 known vulnerabilities. The pure-Python distribution has 0 direct dependencies, requires Python 3.9 or newer, and uses the MIT license. import jmespath completed in 0.12 seconds. The wheel does not include py.typed, so typed projects should expect incomplete package-level checking.
There is no account, credential, daemon, or configuration file. Most setup time goes into learning expression literals and projections. Strings use JMESPath quoting, while JSON numbers, booleans, arrays, and objects commonly sit inside backticks. [*] preserves projection nesting in places where [] flattens a level, so realistic fixtures should cover empty arrays and missing members.
A missing path usually returns Python None rather than raising. That behavior is convenient for optional fields and dangerous for misspellings, so validate results that are required. Built-in functions also enforce JMESPath types at runtime. User-provided expressions cannot call arbitrary Python, but a long expression over a large in-memory document still consumes application CPU.
Call compile() once when one query runs against many records; version 1.1.0 specifically fixes concurrent access to the parser cache. Options can replace the result mapping class or install custom functions. Custom functions require _func_ method names and signature decorators, and the project still calls their API experimental. Boto3 paginator search evaluates the expression once per response page, which can yield individual list elements instead of one combined result.
Patterns
Read a nested value select-nested-field
import jmespath
data = {'user': {'profile': {'name': 'Ana'}}}
name = jmespath.search('user.profile.name', data)
missing = jmespath.search('user.profile.email', data)A missing path returns None, so check `missing` explicitly when the field is required.
Reuse one parsed query compile-expression
expression = jmespath.compile('items[*].id')
for page in pages:
ids = expression.search(page)compile() avoids reparsing the same expression for every document; 1.1.0 also fixes concurrency around the parser cache.
Filter by string and number filter-array
data = {'jobs': [
{'id': 'a', 'state': 'ready', 'retries': 1},
{'id': 'b', 'state': 'failed', 'retries': 4},
]}
ready = jmespath.search("jobs[?state=='ready'].id", data)
retried = jmespath.search('jobs[?retries > `2`].id', data)The numeric value is a JSON literal inside backticks; string comparison uses quoted JMESPath text.
Create a new object shape reshape-result
result = jmespath.search(
'jobs[*].{job_id: id, status: state}',
data
)A multiselect hash builds new mappings and leaves the input dictionaries unchanged.
Flatten one array level flatten-projection
data = {'groups': [
{'users': [{'id': 1}, {'id': 2}]},
{'users': [{'id': 3}]}
]}
nested = jmespath.search('groups[*].users[*].id', data)
flat = jmespath.search('groups[].users[].id', data)The first expression preserves nested lists; empty brackets flatten a projection level and return one list of IDs.
Select the largest item sort-and-pick
data = {'packages': [
{'name': 'a', 'downloads': 10},
{'name': 'b', 'downloads': 40}
]}
name = jmespath.search(
'sort_by(packages, &downloads)[-1].name', data
)sort_by requires the expression to produce comparable values; null or mixed types can raise a runtime type error.
Filter Boto3 paginator output query-boto3-pages
paginator = ec2.get_paginator('describe_instances')
ids = paginator.paginate().search(
"Reservations[].Instances[?State.Name=='running'].InstanceId[]"
)
for instance_id in ids:
print(instance_id)PageIterator.search runs once per API page and yields each element when that page's expression result is a list.
Register a Python function add-custom-function
from jmespath import functions
class Extra(functions.Functions):
@functions.signature({'types': ['string']})
def _func_slug(self, value):
return value.lower().replace(' ', '-')
options = jmespath.Options(custom_functions=Extra())
value = jmespath.search('slug(title)', data, options=options)Custom function support is experimental. Method names must start with `_func_`, and signatures use JMESPath type names.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| jsonpath-ng | PyPI | Choose it for JSONPath syntax, recursive descent, and matches that retain paths into the document. |
| glom | PyPI | Choose it for Python-native nested access, defaults, validation, assignment, and larger transformations. |
| jq | PyPI | Choose the binding when the team already uses jq's broader transformation and streaming language. |
More utils guides
lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

