Skip to main content
jonq - SQL-like JSON query tool for the command line

jonq - readable JSON queries for the terminal

A jq-powered CLI for inspecting, filtering, and reshaping JSON without writing raw jq

PyPI version Python Versions CI tests Documentation Status License: MIT Skylos Grade


What jonq is

jonq is a command-line JSON query tool. It lets you write readable, SQL-like queries such as:

jonq users.json "select name, age if age > 30" -t

Instead of raw jq:

jq '.[] | select(.age > 30) | {name, age}' users.json

jonq compiles your query to jq and executes it with a reusable jq worker. It is useful when you need to understand an unfamiliar JSON payload, extract fields, filter rows, flatten nested arrays, or turn JSON into table, CSV, JSONL, YAML, or raw scalar output.

jonq is not a database, ETL framework, or analytics engine. It is a JSON exploration and shaping tool for terminal workflows.

When to use it

Use jonq when you need to:

  • inspect an API response, config file, generated JSON, or log payload
  • select and rename fields without remembering jq object syntax
  • filter JSON with readable conditions
  • query nested objects and arrays
  • produce table, CSV, JSONL, YAML, raw scalar, or compact JSON output
  • run the same query in shell scripts, CI, or Python code
  • follow NDJSON logs line-by-line

Use another tool when you need:

  • exact jq language control: use raw jq
  • Python expressions over JSON: use jello
  • grep-friendly flattened assignment lines: use gron
  • joins, window functions, or relational analytics: use a database or analytics engine
  • production ETL, scheduling, or connectors: use an ETL system

Install

jonq requires Python 3.9+ and the jq command-line tool.

pip install jonq

From source:

git clone https://github.com/duriantaco/jonq.git
cd jonq
pip install -e .

Check that jq is available:

jq --version

Quick Start

Create a sample file:

cat > users.json <<'JSON'
[
  {"id": 1, "name": "Alice", "age": 30, "city": "New York"},
  {"id": 2, "name": "Bob", "age": 25, "city": "Los Angeles"},
  {"id": 3, "name": "Charlie", "age": 35, "city": "Chicago"}
]
JSON

Select fields:

jonq users.json "select name, age"

Filter rows:

jonq users.json "select name, age if age > 30"

Print raw values for shell pipelines:

jonq users.json "select name" -r

Render a table:

jonq users.json "select name, city, age sort age desc" -t

Get unique values:

jonq users.json "select distinct city"

Aggregate:

jonq users.json "select count(*) as total, avg(age) as avg_age"

See what jq will run:

jonq users.json "select name if age > 30" --explain

Query Syntax

select [distinct] <fields>
  [from <path>]
  [if|where <condition>]
  [group by <fields> [having <condition>]]
  [sort <field> [asc|desc]]
  [limit N]

Examples:

jonq users.json "select *"
jonq users.json "select name as full_name, age"
jonq users.json "select name if city in ('New York', 'Chicago')"
jonq users.json "select name where age > 30"
jonq users.json "select name if not age > 30"
jonq users.json "select name if name like 'Al%'"
jonq users.json "select name if age between 25 and 35"
jonq users.json "select city, count(*) as count group by city"
jonq users.json "select city, avg(age) as avg_age group by city having avg_age > 30"
jonq users.json "select name, age sort age desc limit 2"

Fields and Expressions

Select nested fields with dot notation:

jonq users.json "select profile.email, profile.address.city"

Select from nested arrays with from:

jonq orders.json "select id, total from orders"
jonq users.json "select order_id, price from [].orders if price > 100"

Use array indexes:

jonq users.json "select name, orders[0].item as first_order"

Use functions and expressions:

jonq users.json "select upper(name) as name, str(age) as age"
jonq users.json "select name || ' (' || city || ')' as label"
jonq users.json "select age * 2 + 3 as score"
jonq users.json "select coalesce(nickname, name) as display"
jonq users.json "select case when age > 30 then 'senior' else 'junior' end as segment"

Common functions:

Category Functions
Strings upper, lower, length, trim, ltrim, rtrim
Math round, abs, ceil, floor
Casting int, float, str, string, type
Dates todate, fromdate, date, timestamp
JSON/arrays keys, values, tojson, fromjson, reverse, sort, unique, flatten
Nulls coalesce, is null, is not null

Output Formats

JSON is the default:

jonq users.json "select name, age"

Table:

jonq users.json "select name, age, city" -t
jonq users.json "select name, age, city" --format table

CSV:

jonq users.json "select name, age" --format csv > users.csv

JSONL:

jonq users.json "select name, age" --format jsonl > users.jsonl

YAML:

jonq users.json "select name, age" --format yaml

Raw scalar values:

jonq users.json "select name" -r

Input Sources

Local file:

jonq data.json "select id, name"

Piped stdin:

curl -s https://api.example.com/users | jonq "select id, name" -t
cat data.json | jonq "select id, name where active = true"
cat data.json | jonq - "select id, name"

URL:

jonq https://api.example.com/users.json "select id, email"

Glob:

jonq 'logs/*.json' "select * if level = 'error'"

NDJSON file:

jonq app.ndjson "select level, message if level = 'error'"

Follow live NDJSON from stdin:

tail -f app.ndjson | jonq --follow "select level, message if level = 'error'" -t

Inspect Before Querying

Run jonq with no query to inspect shape, fields, sample values, and suggested queries:

jonq data.json

Example output:

data.json
Root: array of objects (sampled 3 items)

Fields:
  id    number  sample: 1
  name  string  sample: "Alice"
  age   number  sample: 30
  city  string  sample: "New York"

Sample:
  {
    "id": 1,
    "name": "Alice",
    "age": 30,
    "city": "New York"
  }

Suggested queries:
  jonq data.json "select id, name, city" -t
  jonq data.json "select name" -r
  jonq data.json "select city, count(*) as count group by city" -t

Streaming and Watch Modes

Streaming mode processes root-array JSON in chunks:

jonq large.json "select id, name if active = true" --stream

Streaming is for row-wise queries. It intentionally rejects queries that require global input state, including group by, sort, distinct, limit, count, sum, avg, min, max, and count(distinct ...).

Watch mode reruns a query when a file changes:

jonq data.json "select name, age" --watch

Interactive mode provides history and field-aware completion:

jonq -i data.json

Python API

Use query(...) when you want Python data back:

from jonq import query

data = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
]

rows = query(data, "select name if age > 26")
print(rows)

Use execute(...) when you want text output plus metadata:

from jonq import execute

result = execute(data, "select name, age", format="jsonl")
print(result.text)
print(result.compiled.jq_filter)

Compile once and reuse:

from jonq import compile_query, query

compiled = compile_query("select name if age > 25")
print(query([{"name": "Alice", "age": 30}], compiled))

Async helpers are also available: query_async(...) and execute_async(...).

CLI Options

Option Description
-f, --format {json,jsonl,csv,table,yaml} Output format
-t, --table Shorthand for --format table
-r, --raw, --raw-output Print scalar values without JSON quoting
-s, --stream Chunk-safe streaming for root-array JSON
--ndjson Force NDJSON mode
--follow Process NDJSON from stdin line-by-line
-n, --limit N Limit rows after query execution
-o, --out PATH Write output to a file
--jq Print the generated jq filter and exit
--explain Show parsed query details and generated jq
--time Print parse/execute/format timing to stderr
-p, --pretty Pretty-print JSON output
-w, --watch Rerun when the input file changes
--no-color Disable terminal color
--completions {bash,zsh,fish} Print shell completions
--version Print version
-i FILE, --interactive FILE Start the REPL

Shell Completions

# Bash
eval "$(jonq --completions bash)"

# Zsh
eval "$(jonq --completions zsh)"

# Fish
jonq --completions fish > ~/.config/fish/completions/jonq.fish

Troubleshooting

  • jq is not found: Install jq and make sure it is on PATH.

  • No query was provided: Run jonq data.json to inspect the file, or pass a query such as jonq data.json "select *".

  • A field is missing or misspelled: jonq validates fields in selections, filters, sorting, grouping, and aggregations, then suggests close matches and a copy-paste Try: command.

  • Streaming mode rejected a query: Use non-streaming mode for global operations like aggregation, grouping, sorting, distinct, or limit.

  • The generated jq looks surprising: Run the same command with --explain to see the parsed query and generated jq filter.

Known Limitations

  • jonq exposes a practical subset of jq, not the full jq language.
  • Streaming mode supports row-wise queries only.
  • Cross-file joins, window functions, and relational analytics are out of scope.
  • URL fetch is a convenience feature, not a full HTTP client.
  • Very large files can still be slow if the query requires full-input state.

Documentation

License

jonq is licensed under the MIT License. See License.

jonq depends on the jq command-line JSON processor. jq is licensed under the MIT License and is not bundled with jonq.

Metadata

Release files for jonq 0.3.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jonq 0.3.4
File Size Uploaded
jonq-0.3.4.tar.gz 634.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jonq 0.3.4
File Interpreter ABI Platform
jonq-0.3.4-py3-none-any.whl Python 3 none any Details

Total release size: 684.0 kB

Release files / jonq-0.3.4.tar.gz

Download URL jonq-0.3.4.tar.gz
Size 634.8 kB
Tags Source
SHA-256 checksum
How to use checksums
15e5fd20c0ea800ea9c208ddac7b697e1f23c79909b02fafa45fa45de8749907
BLAKE2b-256 checksum
How to use checksums
1ce87500b5fed4f36b830f9594e053b3704963cab1c6f6e612b40f9a4acb1c53
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 30, 2026.

Transparency log

Release files / jonq-0.3.4-py3-none-any.whl

Download URL jonq-0.3.4-py3-none-any.whl
Size 49.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
04dea0e0fe2b47587724bcfbc512d3c14a7d52dc55d21f543eb59c22f2aee65d
BLAKE2b-256 checksum
How to use checksums
d5076fefb88f7db9fffafac2668267ff62d4b297adf3e06390b9068997b64cc2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.4 This release

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.2

1 release file

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page