# Decode a QR code programmatically

> POST image bytes to https://useqr.app/api/v1/decode (raw body, multipart file or JSON url field) to get back every decoded code as JSON — no API key. For local or private decoding, use zxing-cpp or pyzbar in Python, or the zbarimg command line tool. Decoding returns the payload text only, never the styling.

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

---

## The API route

UseQR's decode endpoint is keyless and accepts an image three ways:

```bash
# raw bytes
curl -X POST --data-binary @qr.png -H "Content-Type: image/png" \
  https://useqr.app/api/v1/decode

# multipart form
curl -F "file=@qr.png" https://useqr.app/api/v1/decode

# by URL (GET or JSON body)
curl "https://useqr.app/api/v1/decode?url=https%3A%2F%2Fexample.com%2Fqr.png"
```

The response is JSON:

```json
{ "count": 1, "codes": [ { "text": "https://example.com", "format": "QRCode" } ] }
```

Three numbers worth knowing: images are capped at **10 MB**, the decoder finds up to
**8 codes** in one image, and URL fetches time out after **8 seconds**. Only public
http(s) URLs are fetched — internal and private-range addresses are refused, so you
cannot use the endpoint to probe a network. If no code is found you get a 422 with
concrete fixes (sharper image, crop closer, keep the [quiet zone](/glossary/quiet-zone)
visible) rather than an empty array.

The engine behind it is zxing-wasm — a WebAssembly build of zxing-cpp — running with
`tryHarder` enabled, which costs a little latency and recovers noticeably more damaged
codes. The in-browser [scanner](/scan) uses the same decoder, entirely client-side.

## The local route

Sending images to any server is wrong when they contain credentials or personal data.
Decode locally instead:

**Python, zxing-cpp** (the actively maintained choice):

```python
import zxingcpp, cv2

img = cv2.imread("qr.png")
for result in zxingcpp.read_barcodes(img):
    print(result.text)
```

**Python, pyzbar** (wraps zbar, needs the zbar shared library installed):

```python
from pyzbar.pyzbar import decode
from PIL import Image

for code in decode(Image.open("qr.png")):
    print(code.data.decode())
```

**Command line, zbarimg** (`apt install zbar-tools`, `brew install zbar`):

```bash
zbarimg --raw -q qr.png
```

The trade-offs between these engines are covered in
[ZXing vs quirc vs zbar](/docs/developers/zxing-vs-quirc-vs-zbar).

## Batch decoding

A directory of images is just a loop:

```bash
for f in scans/*.png; do
  printf '%s\t' "$f"
  zbarimg --raw -q "$f" || echo "DECODE FAILED"
done > decoded.tsv
```

The same pattern against the API works when you cannot install anything locally — the
endpoint is keyless, so there is no credential plumbing, and failures arrive as
`application/problem+json` with a `fix` field instead of a bare error string.

## What decoding tells you — and what it cannot

A decoder returns the payload text and the symbology. It does **not** return:

- the [error correction level](/glossary/error-correction-level) or
  [version](/glossary/version) in most library APIs (some expose them, many do not);
- anything about styling — colours, module shapes, logos are invisible to the decoder;
- whether the code *barely* decoded or decoded with margin to spare.

That last gap is why decoding alone is a weak quality gate. A styled code can pass a
clean digital decode and still fail on paper. The stronger pattern — render, rasterise,
decode, compare, and score the margin — is the
[decode-verify loop](/docs/developers/building-a-decode-verify-loop), available as a
service at `GET /api/v1/verify`.

## FAQ

### How do I decode a QR code from an image in Python?
Install zxing-cpp (pip install zxing-cpp) and call zxingcpp.read_barcodes on the loaded image, or use pyzbar with Pillow. Both return the decoded text for every code found in the image.

### Is there an API to decode QR codes?
Yes. POST the image bytes, a multipart file or a JSON url field to https://useqr.app/api/v1/decode. It needs no API key and returns JSON with the decoded text of up to 8 codes per image, with a 10 MB size limit.

### Can I decode a QR code from the command line?
zbarimg from the zbar-tools package decodes image files directly: zbarimg --raw -q qr.png prints the payload. It installs from apt and Homebrew and handles most common image formats.

### Should I decode QR codes locally or via an API?
Decode locally whenever the image may contain credentials, payment details or personal data — the bytes never leave your machine. Use the API when you cannot install native libraries, such as in a restricted CI runner or a no-dependency script.

## Try it

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