jq¶
jqis a command-line JSON processor: it filters, selects, transforms, and reshapes JSON with a small but powerful query language. For anyone who works with APIs, configs, or data exports, jq turns "dump the JSON" into "answer the question" — no Python script required.
Table of Contents¶
- Installation / Setup
- Core Syntax
- Common Operations
- Practical Examples
- Common Patterns
- Best Practices
- Common Mistakes
- Troubleshooting
- Related Cheat Sheets
- Official Documentation
Installation / Setup¶
# Debian/Ubuntu
sudo apt install jq
# macOS
brew install jq
# Windows: winget install jqlang.jq (or use it inside WSL/Git Bash)
jq --version
Core usage: jq 'FILTER' input.json or command | jq 'FILTER'.
Core Syntax¶
| Construct | Syntax | Example |
|---|---|---|
| Identity | . |
jq '.' — pretty-print |
| Key access | .key |
jq '.name' |
| Nested key | .a.b.c |
jq '.train.dataset' |
| Array index | .[0] |
jq '.results[0]' |
| All array elements | .[] |
jq '.results[]' |
| Pipe | \| |
jq '.results[] \| .id' |
| Slice | .[2:5] |
jq '.results[0:3]' |
| Length | length |
jq '.results \| length' |
| Keys | keys |
jq 'keys' |
| Select (filter) | select(condition) |
jq '.[] \| select(.score > 0.8)' |
| Map | map(expr) |
jq '.items \| map(.name)' |
| Sort | sort_by(.score) |
jq 'sort_by(-.score)' |
Common Operations¶
Pretty-print and inspect¶
curl -s https://api.example.com/models | jq '.'
jq '.description, .created_at' response.json
jq 'keys' response.json # what fields does this object have?
jq 'length' response.json # how many items?
Select values from nested structures¶
# Extract every model id and its status
jq '.data[] | {id: .id, status: .status}' response.json
# Flatten nested arrays into a stream of scalars
jq '.data[].metrics' response.json
Filter with conditions¶
# All results above a score threshold
jq '.results[] | select(.score > 0.9) | .text'
# Combine conditions
jq '.results[] | select(.score > 0.9 and .label == "spam")'
Transform and rebuild¶
# Rename keys, keep only some
jq '{model: .name, acc: .metrics.accuracy}' response.json
# Build a CSV line per element
jq -r '.results[] | [.id, .score] | @csv' response.json
-r (raw output) strips quotes so strings are printable.
Practical Examples¶
Example 1 — Inspect an API response¶
curl -s https://api.example.com/v1/search?q=rag | jq '{
count: .total,
top: [.results[0:3][] | {id, score}]
}'
Expected output:
{
"count": 127,
"top": [
{"id": "doc-42", "score": 0.95},
{"id": "doc-17", "score": 0.91},
{"id": "doc-99", "score": 0.88}
]
}
Example 2 — Filter and rank a list¶
# Keep only items above threshold, sorted by score descending
jq '[.results[] | select(.score > 0.8)] | sort_by(-.score)' results.json
Example 3 — Convert JSON to a table¶
# Header + rows, tab-separated
jq -r '["id", "score"] as $h | ($h | @tsv), (.results[] | [.id, .score] | @tsv)' results.json
Expected output:
Common Patterns¶
Count and aggregate¶
jq '[.items[] | .category] | group_by(.) | map({category: .[0], count: length})' data.json
# Sum a field
jq '[.items[].price] | add'
Unique values¶
Build a lookup from an array¶
Pretty-print with sorted keys (diff-friendly)¶
Walk large files (streaming)¶
Best Practices¶
- Always quote the filter in single quotes —
jq '.a'notjq ".a"; shell expansion of$and!will bite you. - Use
-rwhen you want raw strings — pipes, CSV, and file paths need it; pretty JSON doesn't. - Test on a sample first —
head -5 file.json | jq '...'before running on the whole file. - Use
-c(compact) in pipelines — one JSON object per line plays well with other tools and logs. - Prefer
map()/select()over loops — they read as a pipeline, like functional Python. - Validate before transforming —
jq empty file.jsonerrors on invalid JSON (exit 1).
Common Mistakes¶
| Mistake | Consequence | Correct approach |
|---|---|---|
jq .key without quotes |
Shell expands or splits the dot | Always single-quote the filter |
jq '.results[0]' on a non-array |
null or error, silently |
Check the shape first with jq 'type' or keys |
| Expecting array output, getting stream | Wrong number of results in pipelines | Wrap in [...] to force an array |
Using == instead of = in select |
Filter never matches | select(.x == 1); = is assignment |
jq -r '...\| @csv' without -r |
Quotes appear in output | -r for raw output |
| Feeding invalid JSON | parse error: Invalid numeric literal |
Validate: jq empty file.json |
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
jq: error (at <stdin>:1): Cannot index number with "key" |
Field doesn't exist on some elements | Use .[]? or // empty to skip nulls |
parse error: Invalid numeric literal at line 1 |
Input isn't valid JSON (HTML, empty, NDJSON) | Check source; use --stream for huge/NDJSON files |
Nothing printed for a select |
No element matched | Verify the field name and type (jq '.[0] \| type') |
| Output has quotes everywhere | Forgot -r |
Add -r |
jq: command not found |
Not installed / not in PATH | Install per setup; on Windows use WSL or Git Bash |
| Filter works on sample but not the file | Mixed types in the array | jq '.[] \| select(type == "object")' first |
Related Cheat Sheets¶
- Home — repository landing page
- Central Navigation Index — find related categories
- curl — fetch the JSON, then jq it
- wget.md — download JSON files for offline processing
- json.md — JSON structure, serialization, validation
- python-requests.md — the Python equivalent for heavier processing
Official Documentation¶
Last Updated¶
2026-09-07 — Initial version.
Found a mistake or something outdated? Open an issue or submit a pull request — see CONTRIBUTING.md.