jq is a command line tool for working with JSON. Like JSON, jq is ubiquitous, as it comes installed in most Linux distributions, and you would have at least used it to pretty print a JSON file or response:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq

But jq can do more. Much more. jq is on every “five command line tools to learn as a developer” video on YouTube. The Primagen even calls it the BEST CLI tool.

Using jq has been the one of the most useful things I have done in a long time.

In this interactive guide, I will show how you can use jq to do everything from pretty printing JSON to transforming JSON using filters like:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'.[] | select(.address.city == "South Christy") | {name, username, email}'

Or go to the jq Playground instead.

Install jq

This guide lets you to edit and run jq filters directly in your browser. However, if you’d like to follow along on your machine, you can instead install jq.

jq is available on most package managers. You can also download the binary directly or build it from source.

To verify the installation, run:

jq --help

Basic Filters

Identity Filter

The most basic filter in jq is the identity filter ., which represents the current JSON object. It’s the default filter in jq and returns the input JSON unchanged:

echo '{"id": 1,"name":"Leanne Graham","website":"hildegard.org"}' | jq '.'

Notice how jq pretty prints the JSON.

Tip: You can edit these commands. Try editing the above command to see the output without the jq filter.

jq can also apply filters directly to a file (user.json):

{
  "id": 1,
  "name": "Leanne Graham",
  "website": "hildegard.org"
}
jq '.' user.json

Another common usage is to pipe JSON responses from curl commands to jq:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.'

Access Specific Fields

You can access specific fields from JSON objects using the object index filter. Try editing the filter below to access different fields from the JSON object:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.name'

Access Nested Fields

Similarly, you can also access nested fields:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.address.zipcode'

Try accessing the latitude (lat) from the object.

Filter JSON Arrays

JSON arrays commonly represent a list of objects. To access an object at a specific index, use the array index filter:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[0]'

The above example gets the first object from the array. Try changing the index to get the other objects.

Note: Similar to most programming languages, array indexes start at 0 in jq.

Slice JSON Arrays

You can also slice an array into a subarray. For example, you can get the objects from index 3 to 6 (excluding) in a subarray using the array slice filter:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[3:6]'

Tip: You can specify only the start or end of a slice:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[:4]'

You can also specify a negative index to select from the end:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[-6:-3]'

Iterate JSON Arrays

Instead of accessing a single object or a slice from the array, you can use the array value iterator filter to iterate over the entire array. For example, to list the details of all users:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[]'

This is useful when you, say, want to get just the names of all users:

curl 'https://jsonplaceholder.typicode.com/users' | jq '.[].name'

Tip: The -r flag outputs raw strings without quotes.

Construct Objects and Arrays

You can also create JSON objects and arrays with custom fields. This is useful when you only care about specific fields and want to omit others.

Construct New JSON Objects

For example, if you only want the name, email, and company of a user, you can construct a new object using {} with only those fields:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq \
'{"name": .name, "email": .email, "company": .company.name}'

Notice how we created a new emailAddress property.

Construct New JSON Arrays

Similarly, you can construct a new array of objects using []:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'[.[] | {"name": .name, "emailAddress": .email, "company": .company.name}]'

But what’s |?

Combine Filters

The pipe operator | feeds the left filter’s output to the right filter’s input. In the last example, we first use the filter .[] to get the array, and then the filter {"name": .name, "emailAddress": .email, "company": .company.name} to construct a new array.

We have also been combining filters from the beginning. For example, when we access nested fields like this:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.address.zipcode'

We are actually combining two filters:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.address | .zipcode'

Use Functions

jq comes built-in with handy functions that can do a lot of different things.

Get Length

The length function returns the length of a value:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '.name | length'

The below example gets the length of each comment in a post:

curl 'https://jsonplaceholder.typicode.com/posts/1/comments' | jq \
'.[].body | length'

Get Keys

Another useful function is the keys function. As you might have already guessed, it returns the keys of an object in an array:

curl 'https://jsonplaceholder.typicode.com/users/1' | jq '. | keys'

Notice how the keys are sorted alphabetically in the output. You can use the keys_unsorted function instead to preserve the order.

Map Filters

The map function map(f) applies the filter f to each value of an input array or object. The example below shows how a filter is mapped to the entire array of objects:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'map({name: .name, city: .address.city})'

Here’s a better example using a more helpful map. This map will create a “slug” from a user’s name and their city:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'.[:3] |
map({name: .name, city: .address.city, slug: ((.name + "-" + .address.city |
gsub(" "; "-") |
ascii_downcase))})'

The gsub function is a built-in function that’s used for substitutions. Here, we replace empty spaces with - in the slug. The ascii_downcase function then converts the slug to lowercase/downcase.

jq has even more built-in functions that cover a wider array of use cases. I will leave it up to you to explore.

Select Values

The select function is, by a mile, the most useful feature in jq, at least for me. It does what it says: select based on the values of fields.

For example, you can select only the users whose city is “South Christy”:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'.[] | select(.address.city == "South Christy") | {name, username, email}'

The output shows only a single user whose city is “South Christy.” Try using the select function on other JSON.

Transform JSON

In this interactive tutorial, we incrementally built our JSON wrangling skills using jq. Like all command line tools, you build up your skills with practice. You will progressively improve and become a better developer with jq in your arsenal.

So far, we have only managed to scratch the surface of what’s capable with jq. But the part we scratched is a good foundation to learn more and do stuff like:

curl 'https://jsonplaceholder.typicode.com/users' | jq \
'group_by(.address.city) |
map({
  city: .[0].address.city,
  user_count: length,
  users: [.[] | {
    name: .name,
    username: .username,
    slug: (.username + "-" + (.address.city))
  }]
})'

The utility of jq scales with the size of the JSON. There’s nothing like typing a one-liner that gets you the data you want from a 10000-line JSON:

curl 'https://api.github.com/repos/apache/apisix/contributors?per_page=100' | jq \
'sort_by(.contributions) |
reverse |
map({username: .login, contributions}) |
.[0:5]'

You can go further and work with your own examples in the jq Playground.