# 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, 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
