Skip to content

jq

jq is 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

# 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:

id  score
doc-42  0.95
doc-17  0.91

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

jq '[.items[].status] | unique'

Build a lookup from an array

# Convert [{id, name}] into {id: name} mapping
jq 'map({key: .id, value: .name}) | from_entries'

Pretty-print with sorted keys (diff-friendly)

jq -S '.' config.json > config.sorted.json

Walk large files (streaming)

# Don't load the whole array into memory
jq -c '.[] | select(.year > 2020)' huge.ndjson

Best Practices

  • Always quote the filter in single quotes — jq '.a' not jq ".a"; shell expansion of $ and ! will bite you.
  • Use -r when 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.json errors 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

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.