jmespath.py: a JSON query language for Python data structures
JMESPath is a query language for JSON.
At a glance
- What is it?
- jmespath.py implements the JMESPath query language in Python, letting you pull values out of nested dicts and lists with a single expression. It is small, MIT-licensed, and aimed at code that repeatedly reshapes JSON-shaped data.
- Who is it for?
- Adopt jmespath.py when you need to extract values from JSON-shaped dicts and lists repeatedly and want the path expressed as data rather than as nested Python indexing. Do not adopt it when you need to mutate documents, when your data is not JSON-like, or when you want a query language that can join across documents.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 170 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What jmespath.py solves, and who writes JMESPath expressions
Pulling a value out of nested JSON in plain Python means a chain of subscript operations and a mental model of what happens when a key is missing. jmespath.py replaces that chain with a string. Given {"foo": {"bar": "baz"}}, the README states that the expression foo.bar returns "baz". The expression is data, so it can come from a config file, a command-line argument, or a database row instead of being compiled into the code.
The library is for developers who already have Python data structures and want to project them. The README describes the two entry points as functions that "operate on python data structures", which is a narrower claim than it first appears: jmespath.py does not parse JSON text for you. You load the document with json.loads or an HTTP client, then hand the resulting dict to the library. The audience is therefore anyone writing glue code around APIs, logs, or config files where the shape varies but the extraction logic is stable.
How a JMESPath expression is evaluated
The library exposes two functions. jmespath.search(expression, data) parses and evaluates in one call. jmespath.compile(expression) returns a parsed expression object whose search method can be called against many documents. The README is explicit about why the second form exists: it "avoids having to reparse the JMESPath expression each time you search a new document". That is the same split the re module uses for patterns, and it is the right shape when one expression is applied to a stream of records.
The language itself covers more than dotted key lookup. Lists can be indexed with foo.bar[0], and negative indexing is supported, so foo.bar[-1].name returns the last element's name. A wildcard collects across a list: foo.bar[*].name returns ["one", "two"] for a list of objects with name keys. The same star works on hash types, so foo.*.name returns the names of all values under foo. These are the mechanics that make the language worth learning rather than reinventing with a loop.
Evaluation options travel through a jmespath.Options object. The README gives the common case as ordered output of dict keys, using jmespath.Options(dict_cls=collections.OrderedDict). The same Options object also carries custom_functions, which is how the extension mechanism reaches the evaluator.
Installing jmespath.py and running a first query
The README gives one installation route, from PyPI. There is no separate build step and no compiled extension mentioned in setup.py, which lists find_packages and a single script entry.
Custom functions are the extension point, and the README calls them experimental
The built-in function set is defined by the JMESPath specification, but jmespath.py lets you add your own. The README describes four steps: subclass jmespath.functions.Functions, add a method named _func_<name>, decorate it with jmespath.functions.signature to declare argument types, and pass an instance through jmespath.Options(custom_functions=...). The base class introspects its own methods and registers them, so the naming convention is the registration.
The README's own example defines unique_letters, which accepts one string, and my_add, which accepts two numbers. Applied through an Options object, jmespath.search('my_add(`1`, `2`)', {}, options=options) prints 3. This is a real extension surface, but the README states plainly that "custom function support in jmespath.py is experimental and the API may change based on feedback". If your code depends on this path, you are depending on an interface the maintainers have reserved the right to alter. The README also asks that generally useful functions be proposed for the language itself at jmespath.site rather than kept private, which tells you the intended direction is a shared function set, not a per-project dialect.
Where jmespath.py is the wrong tool
JMESPath is a query language for extraction, not transformation. Nothing in the README describes writing values back into a document, deleting keys, or producing a modified structure. If your task is to reshape JSON rather than read from it, this library is the wrong layer.
The second limitation is the input type. The README says the functions operate on Python data structures. There is no streaming parser and no file loader in the documented API, so large documents are your problem to load, and memory use follows from that. A tool that reads JSON lines from a socket and never materialises the whole document will not be helped here.
The third is the experimental custom function API, already noted. A team that needs a stable plugin contract has to accept that the contract may shift. Finally, jmespath.py is one implementation of a specification with a compliance suite. The tests/compliance directory holds .json files grouped by feature so that other implementations can verify identical output. That is a strength for portability, but it also means the language's limits are set by the specification, not by this repository. If you need a feature the specification does not have, this library is not where it will appear first.
jmespath.py against writing the loop yourself
The obvious alternative is plain Python: dict.get calls, list comprehensions, and a helper function. The difference is where the shape of the data lives. With hand-written code the access path is embedded in the logic, so changing which field you read means changing and redeploying code. With JMESPath the path is a string, so it can be stored, passed as an argument, or varied per tenant without a code change.
The cost of that flexibility is a second language in the codebase. Every developer touching the extraction has to know JMESPath syntax, and errors surface as parse failures or unexpected None values rather than as Python exceptions at a line number you can read. For a one-off script that reads two fields, a dict.get chain is shorter and needs no dependency. For a service that applies dozens of configurable projections to API responses, the string form pays for itself.
A second alternative is a general-purpose query tool that operates on JSON text end to end, doing its own parsing and serialisation. jmespath.py deliberately sits one level down, taking already-decoded Python objects. That is a smaller surface and it composes with whatever JSON library you already use, but it means the library will not help with the parsing half of the job.
Maintenance, versioning and the MIT licence
The repository is not archived, and its last push was on 2026-04-20. That is roughly five months before today, so the project sees activity, though the README and setup.py give no release cadence to plan against. setup.py declares version 1.1.0 and python_requires='>=3.9', with classifiers listing CPython and PyPy across Python 3.9 through 3.14. The Python 3.9 floor is the number to check against your own support matrix before adopting.
requirements.txt is a development list, not a runtime list. It pins wheel, pytest, pytest-cov and hypothesis, and adds setuptools and packaging conditionally for Python 3.12 and above with the comment that setuptools is no longer provided by default there. Nothing in the listed dependencies suggests a runtime dependency for the installed package, which keeps the upgrade surface small.
The licence is MIT, declared in setup.py and present as LICENSE at the repository root. MIT is permissive and places few conditions on redistribution, but this is not legal advice; if you vendor the library or ship it inside a product, read the LICENSE file and your own policy. Upgrades should be cheap given the absent runtime dependencies, with the main risk being the experimental custom function API rather than the core search and compile calls.
Editorial conclusion
Adopt jmespath.py when you need to extract values from JSON-shaped dicts and lists repeatedly and want the path expressed as data rather than as nested Python indexing. Do not adopt it when you need to mutate documents, when your data is not JSON-like, or when you want a query language that can join across documents. Before committing, verify the Python version floor in setup.py (python_requires='>=3.9'), confirm that the custom function API, which the README calls experimental, is acceptable for your use, and check whether the pip package you resolve matches version 1.1.0 in setup.py.
Frequently asked questions
What are some JMESPath examples I can try with jmespath.py?
The README works through several: foo.bar returns "baz" from a nested dict, foo.bar[0] returns the first list item, foo.bar[*].name collects the name field from every object in a list, and foo.*.name does the same across the values of a hash. Negative indexing works too, so foo.bar[-1].name returns the last element's name.
How do I install jmespath.py?
The README gives a single command, pip install jmespath, which installs the package from PyPI. setup.py also declares a bin/jp.py script, so the installation places a command-line entry point on your path.
Does jmespath.py parse JSON text for me?
No. The README describes search and compile as functions that operate on Python data structures, so you decode the JSON yourself and pass the resulting dict or list to the library.
Can I add my own functions to a jmespath.py expression?
Yes. Subclass jmespath.functions.Functions, add a method named _func_<name>, decorate it with jmespath.functions.signature, and pass an instance through jmespath.Options(custom_functions=...). The README states that custom function support is experimental and the API may change.
Which Python versions does jmespath.py support?
setup.py sets python_requires='>=3.9' and its classifiers list Python 3.9 through 3.14 for both CPython and PyPy.
What licence does jmespath.py use?
MIT, declared in setup.py and included as the LICENSE file at the repository root.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jmespath-jmespath-py)