# QR codes in a shell script

> Two routes: curl the keyless API (curl -o qr.png "https://useqr.app/q/hello.png") for zero local dependencies, or install qrencode for fully offline generation, including ASCII output straight into the terminal with qrencode -t ANSIUTF8. Loop either over a CSV for batch runs, and verify in CI before shipping.

Source: https://useqr.app/docs/developers/qr-code-in-a-shell-script · Last reviewed 2026-08-21 · UseQR is free forever, MIT licensed, no signup.

---

## The network route: curl

The [keyless API](/docs/developers/free-qr-code-api-no-key) needs no key, no auth header
and no install beyond curl itself:

```bash
curl -o qr.png "https://useqr.app/q/hello.png"

# with options — always url-encode the payload
curl -sG -o qr.png "https://useqr.app/api/v1/qr" \
  --data-urlencode "data=https://example.com/sale?src=poster" \
  --data "size=1024" --data "ec=Q"
```

The `-G --data-urlencode` pair matters: an unencoded `&` in your payload becomes a second
query parameter and silently truncates the code's contents. More curl patterns live on the
[curl page](/docs/developers/generate-a-qr-code-with-curl).

## The offline route: qrencode

`qrencode` is the classic CLI front-end to libqrencode, packaged everywhere
(`apt install qrencode`, `brew install qrencode`). It runs with no network at all, which is
the right choice when the payload is a WiFi password or anything else you should not send
to a server:

```bash
qrencode -o qr.png -s 10 -m 4 -l M "https://example.com"
qrencode -t SVG -o qr.svg "https://example.com"
```

`-s 10` is pixels per module, `-m 4` is the **4-module** [quiet zone](/glossary/quiet-zone),
`-l M` the error-correction level.

## A QR code in your terminal

qrencode's party trick — render the code as text, no image file involved:

```bash
qrencode -t ANSIUTF8 "https://example.com"
```

This prints a scannable code using block characters directly in the terminal. It is
genuinely useful: SSH into a server, generate a code for a URL sitting in a variable, and
scan it off the terminal window with your phone — no file transfer step.

## Batch: loop over a CSV

Given `urls.csv` with `slug,url` per line:

```bash
mkdir -p codes
while IFS=, read -r slug url; do
  curl -sG -o "codes/${slug}.png" "https://useqr.app/api/v1/qr" \
    --data-urlencode "data=${url}" --data "size=1024"
done < urls.csv
```

For hundreds of items, `POST /api/v1/qr/batch` accepts up to 100 items per call and is far
fewer round trips — see [bulk generation](/docs/how-to/how-to-make-qr-codes-in-bulk).

## Verify in CI

A script that generates codes should also prove they decode. The verify endpoint renders,
rasterises and reads the code back with a real decoder, and returns `scannable` in its JSON
report:

```bash
url="https://example.com/download"
curl -sG "https://useqr.app/api/v1/verify" --data-urlencode "data=${url}" \
  | jq -e '.scannable' > /dev/null || { echo "QR failed to decode"; exit 1; }
```

`jq -e` sets the exit code from the value, so a non-scannable code fails the pipeline step.
This is the whole [decode-verify loop](/docs/developers/why-verify-that-your-qr-code-decodes)
in two lines, and it belongs in any pipeline that puts codes in front of users — more
patterns in [QR codes in CI](/docs/developers/qr-codes-in-ci-and-automated-testing).

## Which route when

| Situation | Use |
|---|---|
| Public URL, no install allowed | curl + API |
| Credential or private payload | qrencode (offline) |
| Quick code to scan off a terminal | `qrencode -t ANSIUTF8` |
| Hundreds of codes | `POST /api/v1/qr/batch` |
| Print output | SVG from either route |

## FAQ

### How do I generate a QR code in a bash script?
curl the keyless endpoint and write the bytes to a file, or install qrencode and run it offline. Both are one line; neither needs an API key.

### How do I display a QR code in the terminal?
qrencode -t ANSIUTF8 "your text" prints a scannable code as block characters. It works over SSH and needs no image viewer.

### How do I make QR codes for every row of a CSV?
Loop with while IFS=, read and call curl per row, or send up to 100 items at once to POST /api/v1/qr/batch to cut the round trips.

### Can a script check that a code actually scans?
Yes. GET /api/v1/verify renders and decodes the code server-side and reports scannable true or false; pipe it through jq -e to fail a CI step automatically.

## Try it

- https://useqr.app/url
- https://useqr.app/json
- https://useqr.app/validate
