IAnswerable

Use jq to Stop Squinting at JSON

03 Apr 2026

Piping API responses through python -m json.tool gets you pretty-printing and nothing else. jq gets you filtering, so you can actually find the field you care about in a 5,000-line response.

Install it

brew install jq        # macOS
apt install jq         # Debian/Ubuntu

Pretty-print, the baseline

curl -s https://api.example.com/users | jq

No filter needed — bare jq formats and colorizes. Do this first, every time, before you start writing filters, so you can see the actual shape of the data.

Pull one field

curl -s https://api.example.com/user/1 | jq '.email'

Nested fields chain the same way:

jq '.data.user.email'

jq returns the value with quotes ("dev@example.com"). Add -r for raw output without quotes — you’ll want this constantly when piping into another command:

curl -s https://api.example.com/user/1 | jq -r '.email'

Pull a field out of an array of objects

curl -s https://api.example.com/users | jq '.[].email'

.[] iterates the array; .email grabs the field from each element. This is the single most common pattern you’ll use.

Filter by a condition

curl -s https://api.example.com/users | jq '.[] | select(.active == true)'

select() is jq’s WHERE clause. Chain conditions with and/or:

jq '.[] | select(.active == true and .role == "admin")'

Reshape the output

You often don’t want the whole object — just two or three fields, remapped:

curl -s https://api.example.com/users | jq '.[] | {id: .id, name: .name}'

Or flatten it to a compact array of arrays for a quick table:

jq -r '.[] | [.id, .name, .email] | @csv'

@csv and @tsv turn jq output into something you can pipe straight into a spreadsheet or another script.

Count and aggregate

jq '. | length'                     # array length
jq '[.[] | select(.active)] | length'  # count matching entries
jq '[.[].price] | add'              # sum a field

add on an array of numbers sums them. There’s no built-in sum, this is it.

Sort by a field

jq 'sort_by(.created_at) | reverse'

Combine with .[0] to grab the most recent entry, or .[0:5] to slice the first five.

Read a value straight into a shell variable

STATUS=$(curl -s https://api.example.com/health | jq -r '.status')
if [ "$STATUS" != "ok" ]; then
  echo "unhealthy: $STATUS"
  exit 1
fi

This is the pattern for deploy scripts and health checks — no Python, no extra dependency, jq is already there.

The one that saves the most time: jq 'keys'

When you get a JSON blob you’ve never seen before and don’t want to scroll through it:

curl -s https://api.example.com/whatever | jq 'keys'

Lists the top-level field names in one line. Do this before you write any filter — it tells you exactly what’s available instead of you guessing from a wall of nested braces.

Common failure: Cannot index string with string

Means the JSON at that level is a string, not an object — usually because you filtered too far, or the API returned an error string instead of the object you expected. Drop the last .field in your filter and re-run to see what you’re actually holding at that point.