JSON has won. Whether you're querying cloud infrastructure, debugging APIs, or parsing application logs, you're dealing with JSON. And while grep and awk served us well in the line-oriented world, they falter when faced with nested structures and arrays.
Enter jq - a lightweight, flexible command-line JSON processor that transforms how you work with structured data.
Why jq Matters Now
A decade ago, most CLI tools spoke in plain text. Today, run almost any modern command with --output json or -o json and you'll get structured data:
aws ec2 describe-instances --output json
kubectl get pods -o json
docker inspect container_name
gh api /user
terraform show -json
This shift is a good thing - JSON is unambiguous, parseable, and self-describing. But it also means your old text-processing toolkit needs an upgrade. Piping JSON through grep to find a value works until it doesn't, and it will fail in ways that are hard to debug.
jq gives you a proper query language for JSON, right there in your terminal.
Installation
jq is available in every major package manager:
# macOS
brew install jq
# Debian/Ubuntu
sudo apt install jq
# Fedora
sudo dnf install jq
# Arch
sudo pacman -S jq
# Windows (chocolatey)
choco install jq
Verify it's working:
echo '{"name": "jq"}' | jq '.name'
# "jq"
First Steps: Basic Navigation
Let's start with JSONPlaceholder, a free fake API perfect for learning. Fetch a user:
curl -s https://jsonplaceholder.typicode.com/users/1
That returns a blob of JSON. To extract just the name:
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.name'
# "Leanne Graham"
The . represents the input, and .name navigates to that key. To remove the quotes and get raw text, use -r:
curl -s https://jsonplaceholder.typicode.com/users/1 | jq -r '.name'
# Leanne Graham
Nested access works as you'd expect:
curl -s https://jsonplaceholder.typicode.com/users/1 | jq '.address.city'
# "Gwenborough"
Working with Arrays
Fetch all users:
curl -s https://jsonplaceholder.typicode.com/users | jq '.[0]'
This returns the first user. To get all names:
curl -s https://jsonplaceholder.typicode.com/users | jq '.[].name'
The [] iterates over the array, outputting each element. Combined with .name, you get every user's name, one per line.
To keep the output as a JSON array rather than separate values:
curl -s https://jsonplaceholder.typicode.com/users | jq '[.[].name]'
# ["Leanne Graham", "Ervin Howell", ...]
The outer [] collects the results back into an array.
Filtering with select()
Here's where jq starts to shine. Let's find users whose username starts with a specific letter:
curl -s https://jsonplaceholder.typicode.com/users | jq '.[] | select(.username | startswith("C"))'
Breaking this down:
.[]iterates over each user|pipes each user to the next filterselect()keeps only items where the condition is true.username | startswith("C")checks if the username starts with "C"
Find users in a specific city:
curl -s https://jsonplaceholder.typicode.com/users | jq '.[] | select(.address.city == "Gwenborough")'
Building New Objects
Often you don't want the entire object - you want to reshape the data. Construct new objects with {}:
curl -s https://jsonplaceholder.typicode.com/users | jq '.[] | {name: .name, city: .address.city}'
Output:
{"name": "Leanne Graham", "city": "Gwenborough"}
{"name": "Ervin Howell", "city": "Wisokyburgh"}
...
Wrap it in [] for a proper array:
curl -s https://jsonplaceholder.typicode.com/users | jq '[.[] | {name: .name, city: .address.city}]'
Real-World Example: GitHub API
Let's query something more complex. The GitHub API returns rich, nested data:
curl -s https://api.github.com/users/octocat/repos | jq '.[0]'
Find the 5 most starred repositories:
curl -s https://api.github.com/users/octocat/repos | jq 'sort_by(.stargazers_count) | reverse | .[:5] | .[] | {name: .name, stars: .stargazers_count}'
This pipeline:
1. Sorts by star count
2. Reverses to get descending order
3. Takes the first 5 with .[:5]
4. Iterates and extracts name and stars
Debugging Requests with httpbin
httpbin.org echoes back request information, useful for debugging. See what headers your curl sends:
curl -s https://httpbin.org/headers | jq '.headers'
Test how query parameters are parsed:
curl -s "https://httpbin.org/get?foo=bar&count=42" | jq '.args'
# {"count": "42", "foo": "bar"}
Handling Null and Missing Keys
Real-world JSON is messy. Keys might be missing or null. The ? operator prevents errors:
echo '{"a": 1}' | jq '.b'
# null
echo '[{"a": 1}, {"b": 2}]' | jq '.[].a'
# 1
# null
Use // to provide defaults:
echo '{"a": 1}' | jq '.b // "default"'
# "default"
Combine with select to filter out nulls:
echo '[{"name": "a"}, {"name": null}, {"name": "b"}]' | jq '.[] | select(.name != null) | .name'
# "a"
# "b"
Common Patterns Quick Reference
Count array elements:
jq 'length'
Get unique values:
jq 'unique'
Flatten nested arrays:
jq 'flatten'
Convert object to key-value pairs:
jq 'to_entries'
Group by a field:
jq 'group_by(.field)'
Sum values:
jq '[.[].count] | add'
Check if key exists:
jq 'has("key")'
Compact output (no pretty printing):
jq -c '.'
Putting It Together
Here's a realistic scenario: you want a summary of a GitHub user's repositories, showing only repos with more than 10 stars, sorted by stars, with just the essential info:
curl -s https://api.github.com/users/torvalds/repos | jq '
[.[] | select(.stargazers_count > 10)]
| sort_by(.stargazers_count)
| reverse
| .[]
| {
name: .name,
stars: .stargazers_count,
language: (.language // "not specified"),
url: .html_url
}
'
Next Steps
This post covers the fundamentals, but jq goes much deeper. Advanced features like reduce, walk, recursive descent (..), and custom functions let you handle virtually any JSON transformation.
jq is one of those tools that rewards investment. An hour learning its idioms will save you countless hours of writing throwaway scripts. Start with the basics here, and expand your toolkit as you encounter more complex JSON in the wild.
Want a complete reference you can keep beside your terminal? Our free jq Pocket Reference covers everything from basic filters to advanced techniques, available as PDF and EPUB.