Text & Data

JMESPath Query

Query JSON with JMESPath: filter, project, sort and reshape, and see the result and its type as you type. Uses the JMESPath Community Edition dialect. Runs in your browser.

JMESPath query options

Result indentation

Keyboard shortcuts

Esc
Close a menu or overlay, then leave full screen
↓ on a menu
Open it; ↑ ↓ move through its items
← → in the menu bar
Move between menus

Where your data goes

Using this page Using the API, MCP or A2A
Where it runs In your browser. The JSON is parsed and the JMESPath expression evaluated by JavaScript on this page; nothing is sent anywhere. In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller.
What leaves your device Nothing you enter. After the page and its scripts load, no request carries your input. The browser enforces this: the page's Content-Security-Policy sets connect-src 'none' and form-action 'none', which block fetch, XHR, WebSocket, EventSource, beacons and form posts.

The server receives the JSON document and the expression, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential documents use the page, which does the same work in your browser and sends nothing.

Stored Nothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice. Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country.
Logged Nothing (no requests are made). Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies.
Access Open to anyone. Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare.

Details and how to verify this yourself: Security & privacy.

User guide

The tool sits in a window that works like a desktop app: menus on the top row and the full-screen icon at its right (Esc leaves full screen). The set-up options described below are in Settings → Options…, an overlay, and the Settings menu also lists the common choices directly. To keep the options in view instead, choose View → Inline config; the page remembers that choice. Help → Keyboard shortcuts… lists the keys.

  1. Paste JSON into the left box (or click Sample).
  2. Type a JMESPath expression above it. The result and its type appear on the right as you type. The buttons next to the expression load common patterns.
  3. If the expression is wrong, the error shows the column and the expression with a caret under the problem. If the JSON is wrong, Go to error selects the bad character.

A path that does not exist is not an error: it gives null. A filter that matches nothing gives [].

Three things that trip people up.

  • Literals need backticks. price > `10` compares with the number 10; price > 10 is a syntax error. Strings can be raw literals in single quotes: name == 'bread'. A backtick literal must be valid JSON: `"text"`, `[1, 2]`, `true`.
  • Projections keep going until a pipe. items[*].name[0] applies [0] to each name (a string, so each gives null and is dropped). To take the first element of the list of names, stop the projection with a pipe: items[*].name | [0].
  • Ordering comparisons are for numbers. <, <=, >, >= between strings give null, and a filter treats null as false.

Specification

Dialect. JMESPath Community Edition, as implemented by @jmespath-community/jmespath 1.3 (a tree-walking interpreter: no code generation). It is the original JMESPath language (identifiers, sub-expressions, index and slice expressions, wildcard and flatten projections, filters, multi-select lists and hashes, pipes, ||, &&, !, comparisons, functions, expression references with &) plus these Community extensions:

ExtensionExample
Root reference $ (the whole document, even inside a projection)items[*].[name, $.owner.name]
let expressions with $variableslet $limit = `3` in items[?price > $limit].name
Arithmetic: +, - (also unary), *, /, %, // (integer division)items[2].price * `10`
Ternary condition ? a : b (experimental in the library)length(items) > `2` ? 'many' : 'few'
Raw string literals in single quotes'fresh'

Functions. abs, avg, ceil, contains, ends_with, find_first, find_last, floor, from_items, group_by, items, join, keys, length, lower, map, max, max_by, merge, min, min_by, not_null, pad_left, pad_right, replace, reverse, sort, sort_by, split, starts_with, sum, to_array, to_number, to_string, trim, trim_left, trim_right, type, upper, values, zip. No custom functions are registered. Calling any other name is reported before evaluation, with its position.

Errors.

  • Syntax errors give the column of the token where the problem was detected; an incomplete expression points just past the end. The library’s own messages have no position, so the position is found by re-parsing prefixes of the expression and taking the first one that fails the same way. Unterminated quoted identifiers and invalid backtick literals point at their opening quote or backtick. (The library accepts an unterminated raw string such as 'abc as if it were closed.)
  • Evaluation errors (a function given the wrong type, such as length of a number; division by zero; a slice step of 0) are reported with the position of the function involved when it can be identified.
  • JSON errors give line, column, offset and a JSON Pointer, from the same strict RFC 8259 parser as the JSON tool. Duplicate keys and integers beyond 2^53 − 1 are reported as warnings; the query sees the last value of a duplicate key, and numbers as IEEE 754 doubles.

Result. result is the value; type is its JSON type (object, array, string, number, boolean or null); the formatted text uses 2 or 4 spaces or no whitespace. Object keys that look like array indexes ("2", "10") come first, in numeric order, in any object the query returns: this is how JavaScript orders object keys. Inherited JavaScript property names are never visible: constructor on {} is null.

Examples. All results below are produced by this tool against this document:

{
  "items": [
    { "name": "apple", "price": 1.2, "tags": ["fruit"], "stock": { "qty": 0 } },
    { "name": "bread", "price": 3.5, "tags": ["bakery", "fresh"], "stock": { "qty": 12 } },
    { "name": "cheese", "price": 12, "tags": ["dairy"], "stock": { "qty": 4 } }
  ],
  "owner": { "name": "shop", "email": "[email protected]" }
}
WhatExpressionResult
Field of the first elementitems[0].name"apple"
Negative indexitems[-1].name"cheese"
Projectionitems[*].name["apple","bread","cheese"]
Flatten nested listsitems[].tags[]["fruit","bakery","fresh","dairy"]
Filter by numberitems[?price > `3`].name["bread","cheese"]
Filter by equalityitems[?stock.qty == `0`].name["apple"]
Filter with a functionitems[?contains(tags, 'fresh')].name["bread"]
Combined conditionsitems[?price < `5` && stock.qty > `0`].name["bread"]
Negationitems[?!(stock.qty > `0`)].name["apple"]
Nested filteritems[?tags[?@ == 'fresh']].name["bread"]
Multi-select listitems[*].[name, price][["apple",1.2],["bread",3.5],["cheese",12]]
Multi-select hash (reshape)items[*].{name: name, qty: stock.qty}[{"name":"apple","qty":0},{"name":"bread","qty":12},{"name":"cheese","qty":4}]
Sliceitems[:2].name["apple","bread"]
Reverse sliceitems[::-1].name["cheese","bread","apple"]
Sort by a fieldsort_by(items, &price)[*].name["apple","bread","cheese"]
Largest by a fieldreverse(sort_by(items, &stock.qty))[0].name"bread"
max_by / min_bymax_by(items, &price).name"cheese"
Countlength(items)3
Count matcheslength(items[?stock.qty > `0`])2
Sumsum(items[].stock.qty)16
Maximummax(items[].price)12
Summary object{count: length(items), qty: sum(items[].stock.qty)}{"count":3,"qty":16}
Object keyskeys(owner)["name","email"]
Missing pathitems[0].missingnull
No matchesitems[?price > `100`][]
Default valueowner.phone || 'none'"none"
First non-nullnot_null(owner.phone, owner.email)"[email protected]"
Pipeitems | [0].name"apple"
Projection continuesitems[*].name[0][]
Pipe stops the projectionitems[*].name | [0]"apple"
Join stringsjoin(', ', items[*].name)"apple, bread, cheese"
Membershipcontains(items[*].name, 'bread')true
String functionssplit(owner.email, '@')[1]"example.com"
Map an expressionmap(&upper(name), items)["APPLE","BREAD","CHEESE"]
Object to pairsitems(owner)[["name","shop"],["email","[email protected]"]]
Pairs to objectfrom_items(items[*].[name, price]){"apple":1.2,"bread":3.5,"cheese":12}
Groupgroup_by(items, &to_string(stock.qty > `0`))."true"[*].name["bread","cheese"]
let variablelet $limit = `3` in items[?price > $limit].name["bread","cheese"]
Root referenceitems[*].[name, $.owner.name][["apple","shop"],["bread","shop"],["cheese","shop"]]
Arithmeticitems[2].price * `10`120
Ternarylength(items) > `2` ? 'many' : 'few'"many"
Type of a valuetype(items[0].price)"number"
String comparison'a' < 'b'null

API. POST /api/json/query with input (the JSON text, at most 500,000 characters), expression (at most 2,048 characters) and indent (2, 4, tab or none). The response has result, type, json (the formatted text) and warnings. Invalid JSON, an invalid expression and an evaluation error are 422 errors whose message gives the position.

API & MCP

The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.