Use jq to Stop Squinting at JSON
03 Apr 2026Piping 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.