jq
Tutorial
JSON

jq Tutorial: Step-by-Step Beginner's Guide (2026)

This jq tutorial takes you from zero to writing real filters in about thirty minutes. You will install jq, run your first command, filter arrays, select fields, and parse a live GitHub API response — every step has a command you can copy and the exact output it produces. By the end, you will know enough jq to replace most of the JSON-handling code you write in shell scripts.

May 23, 2026
12 min read
PDF Mavericks Team

Why learn jq

jq is a JSON processor for the command line. It does for JSON what awk does for columns and grep does for lines. If you ever pipe an API response through Python just to pull out one field, this jq tutorial replaces that habit with a single command.

Three places jq earns its keep daily:

  • Parsing API responses — curl returns JSON, jq pulls out the field you want. curl ... | jq -r '.access_token' is shorter than any Python equivalent.
  • Filtering structured logs — apps that emit JSON-per-line logs (kubectl, Docker, Cloud Run) are unreadable with grep. jq filters them by field.
  • JSON-in-shell pipelines — gh, aws, kubectl, terraform output all support JSON. jq slices, joins, and reshapes that output without leaving the shell.

The official project lives at jqlang.github.io/jq. jq is written in C, ships as a single binary, and has no runtime dependencies.

1. Install jq

Pick the line for your OS. Each command installs the official jq build from your package manager:

# macOS
brew install jq

# Ubuntu, Debian
sudo apt-get install jq

# Fedora, RHEL, CentOS
sudo dnf install jq

# Arch
sudo pacman -S jq

# Windows (winget)
winget install jqlang.jq

# Windows (Chocolatey)
choco install jq

Confirm the install:

$ jq --version
jq-1.7.1

If you can't install jq right now — say, you're on a locked-down corporate laptop — skip ahead. The jq playground runs every example in this tutorial in your browser. Nothing gets uploaded.

2. Your first jq command

The smallest useful jq command is . — the identity filter. It reads JSON in, pretty-prints it out.

1
Pretty-print JSON

The . filter is the identity. Whatever JSON goes in, jq prints it back formatted and color-coded.

Command
echo '{"name":"Jane","age":28}' | jq '.'
Output
{
  "name": "Jane",
  "age": 28
}
2
Extract one field

Use .field to pull a single value out of an object. The dot prefix is required — it tells jq to walk into the object.

Command
echo '{"name":"Jane","age":28}' | jq '.name'
Output
"Jane"
3
Get a raw string

By default, jq wraps strings in quotes. Add -r (raw output) when you want plain text — useful when piping into other commands.

Command
echo '{"name":"Jane","age":28}' | jq -r '.name'
Output
Jane
4
Read from a file

Pass a file path as the last argument. jq reads from stdin only if no file is given.

Command
jq '.name' user.json
Output
"Jane"

3. Filtering arrays

Most real-world JSON is an array of objects — a list of users, a list of pods, a list of orders. jq has dedicated syntax for arrays:

5
Iterate every element with .[]

.[] takes an array and emits each element as a separate output. jq prints each on its own line.

Command
echo '[{"name":"A"},{"name":"B"},{"name":"C"}]' | jq '.[]'
Output
{
  "name": "A"
}
{
  "name": "B"
}
{
  "name": "C"
}
6
Get the first or last element

Index by position with .[N]. Negative indices count from the end — .[-1] is the last element.

Command
echo '[10,20,30,40]' | jq '.[0], .[-1]'
Output
10
40
7
Slice a range

.[2:5] returns elements from index 2 up to but not including index 5 — same convention as Python.

Command
echo '[10,20,30,40,50,60]' | jq '.[2:5]'
Output
[
  30,
  40,
  50
]
8
Count elements

length returns the array length when applied to an array. Works on strings and objects too — count keys, count characters.

Command
echo '[1,2,3,4,5]' | jq 'length'
Output
5

4. Selecting fields and nested keys

jq follows JSON's tree structure with dot notation. The same syntax works at any depth.

9
Walk into a nested object

Chain dots to reach deep keys. Each dot moves one level down.

Command
echo '{"user":{"profile":{"city":"Mumbai"}}}' | jq -r '.user.profile.city'
Output
Mumbai
10
Optional access with ?

Append ? to suppress errors when a key is missing. jq returns null instead of failing the pipeline.

Command
echo '{"user":{}}' | jq '.user.profile?.city'
Output
null
11
Pick multiple fields into a new object

Object construction with {key: .value} builds a new object from selected fields. Strips out anything you didn't ask for — useful for trimming sensitive data.

Command
echo '{"id":42,"name":"Jane","email":"j@x.com","password":"hunter2"}' | jq '{id, name, email}'
Output
{
  "id": 42,
  "name": "Jane",
  "email": "j@x.com"
}
12
Delete a key

del(.key) removes a key in place. Combine with paths like del(.user.password) for nested deletes.

Command
echo '{"name":"Jane","password":"hunter2"}' | jq 'del(.password)'
Output
{
  "name": "Jane"
}

5. Transformations: map, select, with_entries

These three filters do most of the work in real jq scripts. Learn them and you cover the long tail of day-to-day queries.

13
map(f) — apply a filter to every element

map runs the filter on each array element and collects the results into a new array. Equivalent to [.[] | f].

Command
echo '[{"name":"A"},{"name":"B"}]' | jq 'map(.name)'
Output
[
  "A",
  "B"
]
14
select(condition) — keep only matching elements

select acts as a filter. The pipeline keeps the input only if the condition is true. Combine with .[] to filter an array.

Command
echo '[{"role":"admin"},{"role":"viewer"},{"role":"admin"}]' | jq '[.[] | select(.role == "admin")]'
Output
[
  {
    "role": "admin"
  },
  {
    "role": "admin"
  }
]
15
with_entries — transform every key/value pair

with_entries converts the object to an array of {key, value} pairs, maps your filter over them, then rebuilds the object. Used to rename keys or transform values uniformly.

Command
echo '{"first":"jane","last":"doe"}' | jq 'with_entries(.value |= ascii_upcase)'
Output
{
  "first": "JANE",
  "last": "DOE"
}
16
Sort and group

sort_by(.field) sorts an array by a field. group_by(.field) buckets elements that share a value.

Command
echo '[{"d":"e","n":"A"},{"d":"s","n":"B"},{"d":"e","n":"C"}]' | jq 'group_by(.d)'
Output
[
  [
    {
      "d": "e",
      "n": "A"
    },
    {
      "d": "e",
      "n": "C"
    }
  ],
  [
    {
      "d": "s",
      "n": "B"
    }
  ]
]

6. Real-world examples

The examples above are warm-ups. Here is jq doing the work you will actually use it for.

17
Parse a GitHub API response

GitHub returns JSON for every endpoint. This pulls the names and star counts of your most-starred repos. -r strips the surrounding quotes; @tsv tab-separates the fields for downstream tools like awk or column.

Command
curl -s https://api.github.com/users/torvalds/repos \
  | jq -r '.[] | [.name, .stargazers_count] | @tsv' \
  | sort -k2 -rn | head -5
Output
linux	176432
libdc-for-dirk	298
subsurface	271
test-tlb	148
uemacs	720
18
Filter kubectl get pods output

kubectl -o json emits the full pod spec for every pod in the namespace. jq narrows it to a table of pod name and current phase — useful in a status check script.

Command
kubectl get pods -o json \
  | jq -r '.items[] | [.metadata.name, .status.phase] | @tsv'
Output
api-7d4c8f9b6c-x4k2m	Running
api-7d4c8f9b6c-z8n3p	Running
worker-6f8b7d5c4f-q9w2r	Pending
worker-6f8b7d5c4f-t3y5u	CrashLoopBackOff
19
Extract a token from an auth response

Auth endpoints return JSON containing the bearer token. jq -r pulls the raw string so you can assign it to a shell variable and reuse it in the next curl call.

Command
TOKEN=$(curl -s -X POST https://api.example.com/auth \
  -d '{"user":"jane","pass":"x"}' \
  | jq -r '.access_token')

echo "Got token: $TOKEN"
Output
Got token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
20
Count failed pods only

Combines select to filter the array, then length on the result. Drop-in replacement for a shell loop with counters.

Command
kubectl get pods -o json \
  | jq '[.items[] | select(.status.phase != "Running")] | length'
Output
2
21
Convert JSON logs to CSV

When you need to load JSON-per-line logs into a spreadsheet, jq's @csv handles quoting and escaping for you. The -r is required so the output isn't itself wrapped in quotes.

Command
cat app.log \
  | jq -r '[.timestamp, .level, .message] | @csv'
Output
"2026-05-23T10:14:02","INFO","login ok"
"2026-05-23T10:14:09","ERROR","db timeout"
"2026-05-23T10:14:11","INFO","login ok"

7. Run jq in your browser

Every command in this jq tutorial runs in our jq playground. The playground compiles jq to WebAssembly so the filter executes inside your browser tab — your JSON never leaves your device. That matters when the JSON is a production API response, an auth payload, or anything you would rather not paste into someone else's server.

Workflow when you're learning:

  1. Copy a filter from this page.
  2. Paste your real JSON into the input pane.
  3. Edit the filter until the output looks right.
  4. Copy the working filter into your shell script.

Once the output is in the shape you want, you can paste it into our JSON formatter to validate, minify, or compare against another JSON tree.

Practical tips after the basics

Build filters one stage at a time

Start with '.', confirm the input looks right, then add one pipe at a time. Run after each addition. If the output suddenly goes empty, the most recent stage is the suspect.

Use type to debug pipe stages

When a filter returns null or fails, replace it with 'type' to see what jq thinks the input is. 'string' vs 'array' vs 'null' often explains the error in two seconds.

Save filters to a .jq file

Complex filters get hard to read in a single line. Save them to a file and run jq -f filter.jq input.json — easier to edit, easier to commit to git, easier to share with teammates.

Quote single-quotes carefully on Windows

PowerShell handles single-quote arguments differently from bash. On Windows, wrap the filter in double quotes and escape inner quotes, or run jq inside WSL where the bash syntax just works.

Pipe jq output into the next tool

jq plays well with awk, column, sort, uniq, and shell loops. The -r flag is critical here — without it, every value comes wrapped in quotes and the next tool sees them as part of the data.

Try every example in your browser

Open the jq playground in a new tab. Paste any command from this tutorial. See the output instantly — your JSON stays on your device.

Open jq Playground