CLI AI

Filter JSON on the command line with jq

2026-05-25

Pull a value out of JSON with jq '.users[0].name' api.json — the dot is jq's path syntax, and everything past it (select, @csv, -r) is the same idea applied to filtering, converting, or cleaning up the output. jq treats JSON the way awk treats text: parse once, then pipe filters together. The six patterns below cover what actually comes up when you're staring at an API response and need one field, a filtered list, or a CSV out of it.

1. Get one field

clai
$ clai get the first user's name from api.json→ jq '.users[0].name' api.json"alice"

The dot is jq's filter for "take the current value," and a path follows it. The quotes in the output mean jq returned a JSON string, not plain text for the shell.

2. Strip quotes for scripting

clai
$ clai print all user names without quotes→ jq -r '.users[].name' api.jsonalicebob

-r (raw output) gives you bare strings, which is what you want when the result feeds a shell variable or a loop. Skip -r and you get quoted strings that fail your first comparison for no obvious reason.

3. Filter by a condition

clai
$ clai show the names of users older than 30→ jq -r '.users[] | select(.age > 30) | .name' api.jsonalice

.users[] unrolls the array into a stream of values, and select() only lets the matching ones through. The pipe inside jq works exactly like a shell pipe.

4. Turn it into CSV

clai
$ clai turn this into csv with name and age→ jq -r '.users[] | [.name, .age] | @csv' api.json"alice",34"bob",28

Collect the fields you want into an array and hand it to @csv. There's also @tsv, @base64, and @uri. That's one line instead of a Python script.

5. Make sense of an unfamiliar API response

clai
$ clai show what keys exist at the top level→ jq -r 'keys[]' api.jsonusers

This is the first thing worth running against someone else's JSON. Follow it with jq -r '.users[0] | keys[]' to see the fields inside one element.

6. Parse an API response straight from curl

clai
$ clai get json from the api and print just the version field→ curl -s https://example.com/api/status | jq -r '.version'

This combination is the whole reason jq exists. -s turns off curl's progress meter — without it, that meter lands in the stream and jq chokes on it.

Gotchas

  • -r changes the output, not the filter. A missing -r is behind most of the "1.2.3"-in-a-string-comparison weirdness in bash scripts.
  • A key with a dash or a dot needs quotes: jq '."content-type"'. Without them jq tries to parse it as an expression and fails.
  • jq chokes on a stream of several JSON objects. For line-delimited JSON (JSON Lines), add -s (slurp) or process it line by line.

Related questions

How do I get the last element of an array? jq '.users[-1]'. Negative indices count from the end.

How do I check whether a field exists? jq 'has("age")' on an object, or jq '.users[] | select(has("email"))' to filter a list.

jq or python -m json.tool? json.tool only formats. jq selects, filters, and transforms — different jobs in a pipeline.

See also

Describe the field you need and CliAI writes the jq filter, quotes included. Install it in one line.