# Swiss QR-bill Reader (PDF / image to JSON) (`najx/swiss-qr-bill-reader`) Actor

Read Swiss QR-bills from PDF invoices or images and get the payment data as JSON: IBAN or QR-IBAN, amount, currency, QR or creditor reference, creditor, debtor, message and Swico billing information, validated. Batch-ready for accounts-payable automation.

- **URL**: https://apify.com/najx/swiss-qr-bill-reader.md
- **Developed by:** [Najim Abdelmoula](https://apify.com/najx) (community)
- **Categories:**
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 swiss qr-bill readers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Swiss QR-bill Reader do?

**Swiss QR-bill Reader** reads the **Swiss QR code** printed on invoices (QR-facture, QR-Rechnung, QR-fattura) and returns the payment data as clean JSON: **IBAN or QR-IBAN, amount, currency, QR reference or creditor reference, creditor, debtor, message and Swico billing information**. Give it PDF files or images, scanned or native, one or hundreds, and get one validated row per QR-bill. It is a **Swiss QR-bill OCR alternative**: no character recognition, the data comes straight from the QR code, so it is exact.

It does not pay anything, does not talk to your bank, and keeps nothing after the run.

### Why use Swiss QR-bill Reader?

Every supplier invoice in Switzerland carries a QR-bill since 2022. Typing its IBAN, reference and amount into accounting software is slow and error-prone; the QR code already contains all of it, character-exact.

- 📥 **Accounts payable without typing**: decode incoming invoices from an e-mail inbox, a shared folder or a document management system and push the fields to your ERP, Bexio, Abacus, Banana or a payment file.
- ✅ **Validated data**: IBAN check digits, 27-digit QR reference modulo-10 check, ISO 11649 creditor reference check, account type versus reference type, currency and amount format. Problems are reported as warnings or errors per bill.
- 📄 **PDF or image, native or scanned**: pages are rendered and decoded at the resolution you choose; rotated or slightly skewed codes are handled.
- 🤖 **Automation-friendly**: call it from n8n, Make, Zapier or the Apify API, or let an AI agent read a bill through the Apify MCP server. Structured input, structured output, no OCR service to host.
- 🔍 **Audit trail**: the exact Swiss Payments Code decoded from the QR is returned with each row.

Typical users: fiduciaries and accountants, SMEs automating supplier invoices, ERP and DMS integrators, property managers, and AI assistants that book or pay invoices.

### What data does it extract?

| Field | Type | Description |
| --- | --- | --- |
| `file` / `page` | string / integer | Source file name and page where the QR-bill was found |
| `status` | string | `ok`, `invalid` (errors, or warnings in strict mode), `not_found`, `error` |
| `account` / `accountType` | string | IBAN, and whether it is a `QR-IBAN` or a regular `IBAN` |
| `amount` / `currency` | number / string | Amount (empty when left to the payer) and CHF or EUR |
| `referenceType` / `reference` | string | `QRR` (27-digit QR reference), `SCOR` (RF creditor reference) or `NON` |
| `creditor` / `debtor` | object | Name, street, house number, postal code, city, country (or line1/line2 for combined addresses) |
| `additionalInformation` | string | Unstructured message |
| `billingInformation` | string | Swico structured billing information (`//S1/...`) |
| `alternativeProcedures` | array | Alternative procedure parameters, when present |
| `rawPayload` | string | The exact Swiss Payments Code from the QR code |
| `warnings` / `errors` | array | Validation messages |

### How to read a Swiss QR-bill

1. Open the Actor and paste the **URLs** of your PDF invoices or images in **File URLs** (public links, or files uploaded to an Apify key-value store).
2. Optionally restrict the **pages** to scan and raise the **dpi** for small or blurry codes.
3. Click **Start**. Each QR-bill found appears in the Output tab as one row.
4. Download the table as JSON, CSV or Excel, or read it from your automation through the API.

#### Where do I get file URLs from?

Any public link works. From n8n, Make or Zapier, upload the incoming PDF to an Apify key-value store with the Apify integration and pass the record URL. From code, use the Apify client to store the file, then call the Actor with its URL.

#### What is the difference between a QR-IBAN and an IBAN?

A **QR-IBAN** (bank identification 30000 to 31999) always comes with a 27-digit **QR reference** that your supplier uses to match your payment. A regular **IBAN** comes with an **RF creditor reference** or no reference. The Actor checks that the pair is consistent and warns you when it is not, because banks reject such payments.

### How much does it cost?

The Actor is **paid per decoded QR-bill** (see the pricing box on this page). Files without a QR-bill, unreadable files and invalid bills are free. Reading 100 supplier invoices costs a few cents; the Apify free plan covers a small business's monthly volume.

### Input

See the Input tab for all options. Minimal example:

```json
{
  "files": [
    "https://example.com/invoices/2026-0042.pdf",
    "https://example.com/invoices/scan-0043.jpg"
  ]
}
```

### Output

You can download the dataset in JSON, CSV, Excel or HTML formats. One decoded bill and one file without QR-bill:

```json
[
  {
    "file": "2026-0042.pdf",
    "page": 1,
    "status": "ok",
    "version": "0200",
    "account": "CH4431999123000889012",
    "accountType": "QR-IBAN",
    "creditor": {"type": "structured", "name": "Robert Schneider AG", "street": "Rue du Lac", "houseNumber": "1268", "postalCode": "2501", "city": "Biel", "country": "CH"},
    "amount": 1949.75,
    "currency": "CHF",
    "debtor": {"type": "structured", "name": "Pia-Maria Rutschmann-Schnyder", "street": "Grosse Marktgasse", "houseNumber": "28", "postalCode": "9400", "city": "Rorschach", "country": "CH"},
    "referenceType": "QRR",
    "reference": "210000000003139471430009017",
    "additionalInformation": "Order of 15 June 2026",
    "billingInformation": null,
    "alternativeProcedures": [],
    "rawPayload": "SPC\r\n0200\r\n1\r\nCH4431999123000889012\r\nS\r\n...",
    "warnings": [],
    "errors": []
  },
  {
    "file": "terms.pdf",
    "status": "not_found",
    "pagesScanned": 3,
    "warnings": [],
    "errors": []
  }
]
```

### Using the Actor from code and AI agents

Call it through the [Apify API](https://docs.apify.com/api/v2) in any language, or expose it to Claude, ChatGPT, Cursor or any MCP client through the [Apify MCP server](https://mcp.apify.com/). Example with the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("<ACTOR_ID>").call(run_input={  # Actor ID or "username/name" from the API tab
    "files": ["https://example.com/invoices/2026-0042.pdf"],
})
for bill in client.dataset(run["defaultDatasetId"]).iterate_items():
    if bill["status"] == "ok":
        print(bill["account"], bill["amount"], bill["currency"], bill["referenceType"], bill["reference"])
```

### Privacy and data handling

Files are downloaded into the memory of your own Actor run, decoded and discarded. Only the extracted fields are written to the run's dataset, which belongs to your Apify account and follows your retention settings. The Actor author never receives, logs or stores your invoices. Invoices contain personal data of your suppliers and of yourself: process them under your own legal basis and delete the run's dataset when you no longer need it.

### Frequently asked questions

#### The QR code is not found

Make sure the page containing the QR-bill is within the scanned pages (first 20 by default), raise `dpi` to 300 for small or blurry scans, and check that the file is a PDF or a supported image. Photos taken at an angle may need a straighter shot.

#### Can it read several QR-bills in one file?

Yes. Each distinct QR-bill found in a file becomes one row, with its page number.

#### Can it generate QR-bills too?

A companion Actor from the same author generates Swiss QR-bills as PDF, SVG or PNG from JSON or CSV.

#### Something looks wrong

Open an issue in the **Issues** tab with a minimal example (without real supplier data).

### Development

Python 3.12, Apify SDK, `pypdfium2` for PDF rendering, `zxing-cpp` for QR decoding, Pillow for images; the Swiss Payments Code parser and validators live in `spc.py`. `pytest tests` runs against reference bills; `apify run` runs the Actor locally.

# Actor input Schema

## `files` (type: `array`):

Public URLs of PDF, PNG, JPEG, TIFF, GIF, BMP or WebP files, or Apify key-value store record URLs. One row is produced per Swiss QR-bill found; files without a QR-bill are reported as not\_found.

## `pages` (type: `string`):

Page range such as "1", "1-3" or "1,3-4". Leave empty to scan the first 20 pages. Scanning fewer pages is faster; the QR-bill is usually on the last page of an invoice, so "-1" style ranges are not supported: give the page numbers.

## `dpi` (type: `integer`):

Resolution used to render PDF pages before decoding. 200 works for native and good scans; raise to 300 for small or blurry codes.

## `strict` (type: `boolean`):

When true, a QR-bill with warnings (e.g. QRR reference on a regular IBAN, unexpected currency) is reported as invalid instead of ok.

## `includeRawPayload` (type: `boolean`):

Adds the exact text decoded from the QR code (rawPayload) to each row, useful for archiving and troubleshooting.

## Actor input object example

```json
{
  "dpi": 200,
  "strict": false,
  "includeRawPayload": true
}
```

# Actor output Schema

## `bills` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("najx/swiss-qr-bill-reader").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("najx/swiss-qr-bill-reader").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call najx/swiss-qr-bill-reader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,najx/swiss-qr-bill-reader"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/z0WhECbRdIHEyXuu4/builds/1HPyNCUJYUyVht7ha/openapi.json
