JMESPath
JMESPath (pronounced 'james path') is a query language for JSON that allows you to declaratively extract, filter, and transform elements from JSON documents or Python dictionaries. Current stable version is 1.0.0 (1.1.0 on PyPI as of 2026). The project is mature and low-churn — it reached 1.0 in 2022 with no breaking API changes, and releases are infrequent. It is a foundational dependency of boto3/botocore and is used by the AWS CLI --query flag.
Warnings
- breaking jmespath.search() requires a parsed Python dict/list, NOT a raw JSON string. Passing a JSON string silently returns None or wrong results.
- breaking Python 2 and Python <3.7 support was dropped in 1.0.0. The PyPI package requires Python >=3.9 as of 1.1.0.
- gotcha Indexing into a wildcard projection (e.g. people[*].name[0]) does NOT return the first element of the projected list — it attempts to index each string, returning [].
- gotcha [] (flatten) and [*] (wildcard) are NOT equivalent. [] flattens one level of nested arrays; [*] keeps the original list structure intact.
- deprecated Custom function support is explicitly marked experimental by the authors; the API (signature decorator, _func_ naming) may change without a major version bump.
- gotcha Calling jmespath.search() with the same expression string in a hot loop re-parses the expression on every call, causing significant overhead.
- gotcha Numeric literals in filter expressions must be wrapped in backticks, not quotes. people[?age > '18'] compares against a string; people[?age > `18`] compares against a number.
Install
-
pip install jmespath
Imports
- jmespath
import jmespath
- functions.Functions
from jmespath import functions
- functions.signature
from jmespath.functions import signature
- Options
jmespath.Options(dict_cls=..., custom_functions=...)
Quickstart
import jmespath
from jmespath import functions
# 1. One-shot search
data = {
"people": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 17},
{"name": "Carol", "age": 25},
]
}
# Returns all names
names = jmespath.search("people[*].name", data)
print(names) # ['Alice', 'Bob', 'Carol']
# Filter: only adults
adults = jmespath.search("people[?age >= `18`].name", data)
print(adults) # ['Alice', 'Carol']
# 2. Compile once, search many times (avoids re-parsing)
expr = jmespath.compile("people[*].age")
print(expr.search(data)) # [30, 17, 25]
# 3. Pipe to index into a projection result (not people[*].name[0]!)
first_name = jmespath.search("people[*].name | [0]", data)
print(first_name) # 'Alice'
# 4. Custom function via Options
class MyFunctions(functions.Functions):
@functions.signature({"types": ["string"]})
def _func_upper(self, s):
return s.upper()
opts = jmespath.Options(custom_functions=MyFunctions())
result = jmespath.search("people[0].name | upper(@)", data, opts)
print(result) # 'ALICE'