Tutorial · 6 Min. Lesezeit

Schweizer Lohnausweis automatisch auslesen — Schritt-für-Schritt mit der datawork.dev API

Wie man einen Scan des Schweizer Lohnausweises (ESTV Formular 11) hochlädt, Felder wie Bruttolohn, AHV-Nummer und Abzüge extrahiert — und das Ergebnis als strukturiertes JSON mit Bildausschnitten erhält.

APIOCRJSONLohnausweisESTVBruttolohnPythonJavaScript

Der Schweizer Lohnausweis (ESTV Formular 11) ist ein standardisiertes Dokument, das jeder Arbeitgeber jährlich ausstellen muss. HR-Abteilungen, Treuhänder und Steuerberater verarbeiten täglich grosse Mengen davon — manuelles Abtippen ist fehleranfällig und zeitaufwendig.

Mit der datawork.dev API lässt sich ein Scan oder PDF des Lohnausweises in wenigen Zeilen Code automatisch auslesen — mit allen relevanten Ziffern und Feldern, direkt als JSON.

1. Der Schweizer Lohnausweis — Felder im Überblick

Das ESTV-Formular 11 ist in Kopfbereich und nummerierte Ziffern aufgeteilt. Folgende Felder extrahieren wir in diesem Tutorial:

Kopfbereich

arbeitgeber_name Name des Arbeitgebers
arbeitgeber_adresse Adresse des Arbeitgebers
arbeitnehmer_name Name des Arbeitnehmers
arbeitnehmer_adresse Adresse des Arbeitnehmers
ahv_nummer 13-stellige AHV-Nummer
beschaeftigungsjahr Steuerjahr / Zeitraum

Ziffern (Lohnbestandteile)

ziffer_1_bruttolohn Ziffer 1 — Bruttolohn
ziffer_2_verpflegung Ziffer 2 — Verpflegung/Unterkunft
ziffer_7_andere Ziffer 7 — Andere Leistungen
ziffer_9_ahv_iv_eo Ziffer 9.1 — AHV/IV/EO-Abzug
ziffer_9_alv Ziffer 9.2 — ALV-Abzug
ziffer_9_pensionskasse Ziffer 9.3 — Pensionskasse
ziffer_9_nbuv Ziffer 9.4 — NBUV-Abzug
ziffer_10_nettolohn Ziffer 10 — Nettolohn
ziffer_13_quellensteuer Ziffer 13 — Quellensteuer

2. Voraussetzungen

Sie benötigen ein kostenloses Konto auf datawork.dev. Die API kostet CHF 0.15 pro Seite — Pay-as-you-go, keine Kreditkarte für die Registrierung nötig.

  • Kostenloses Konto auf datawork.dev
  • Scan oder PDF des Lohnausweises (JPG, PNG oder PDF)
  • curl, JavaScript (Node.js / Browser) oder Python

3. Authentifizierung

Die API verwendet JWT-Token. Der Token ist 24 Stunden gültig.

curl
curl -X POST https://datawork.dev/api/login \
  -d "username=ihre@email.ch" \
  -d "password=IhrPasswort"
JavaScript
const res = await fetch('https://datawork.dev/api/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: 'username=ihre@email.ch&password=IhrPasswort'
});
const { access_token } = await res.json();
Python
import requests

res = requests.post('https://datawork.dev/api/login', data={
    'username': 'ihre@email.ch',
    'password': 'IhrPasswort'
})
token = res.json()['access_token']

4. Scan hochladen & Felder definieren

Der Endpunkt POST /api/extract/analyze/async nimmt den Scan als Multipart-Upload entgegen. Mit fields definieren Sie, welche Felder extrahiert werden sollen, und mit includeImages=true erhalten Sie zusätzlich einen Bildausschnitt pro Feld zur Verifikation.

curl
curl -X POST https://datawork.dev/api/extract/analyze/async \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@lohnausweis_2024.pdf" \
  -F "fields=arbeitgeber_name,arbeitnehmer_name,ahv_nummer,beschaeftigungsjahr,ziffer_1_bruttolohn,ziffer_9_ahv_iv_eo,ziffer_9_alv,ziffer_9_pensionskasse,ziffer_10_nettolohn,ziffer_13_quellensteuer" \
  -F "includeImages=true"
JavaScript
const felder = [
  'arbeitgeber_name', 'arbeitnehmer_name', 'ahv_nummer', 'beschaeftigungsjahr',
  'ziffer_1_bruttolohn', 'ziffer_9_ahv_iv_eo', 'ziffer_9_alv',
  'ziffer_9_pensionskasse', 'ziffer_10_nettolohn', 'ziffer_13_quellensteuer'
];

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('fields', felder.join(','));
formData.append('includeImages', 'true');

const res = await fetch('https://datawork.dev/api/extract/analyze/async', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}` },
  body: formData
});
const { jobId } = await res.json();
console.log('Job gestartet:', jobId);
Python
FELDER = [
    'arbeitgeber_name', 'arbeitnehmer_name', 'ahv_nummer', 'beschaeftigungsjahr',
    'ziffer_1_bruttolohn', 'ziffer_9_ahv_iv_eo', 'ziffer_9_alv',
    'ziffer_9_pensionskasse', 'ziffer_10_nettolohn', 'ziffer_13_quellensteuer'
]

with open('lohnausweis_2024.pdf', 'rb') as f:
    res = requests.post(
        'https://datawork.dev/api/extract/analyze/async',
        headers={'Authorization': f'Bearer {token}'},
        files={'file': ('lohnausweis.pdf', f, 'application/pdf')},
        data={'fields': ','.join(FELDER), 'includeImages': 'true'}
    )

job_id = res.json()['jobId']
print(f'Job gestartet: {job_id}')
Tipp: Die Feldnamen können Sie frei wählen — auf Deutsch oder Englisch. Die KI versteht den Kontext und ordnet die Felder automatisch den richtigen Ziffern im Lohnausweis-Formular zu. Für mehrseitige PDFs (z.B. mit Beiblatt) werden alle Seiten automatisch verarbeitet.

5. Ergebnis abrufen (Polling)

Die Verarbeitung läuft asynchron. Fragen Sie den Status per GET /api/extract/jobs/{jobId} ab — sobald status === "DONE" ist das Ergebnis verfügbar. Typischerweise dauert die Verarbeitung 3–8 Sekunden pro Seite.

JavaScript — Polling
async function pollResult(jobId, token, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    const res = await fetch(`https://datawork.dev/api/extract/jobs/${jobId}`, {
      headers: { 'Authorization': `Bearer ${token}` }
    });
    const data = await res.json();

    if (data.status === 'DONE')   return data;
    if (data.status === 'FAILED') throw new Error(data.errorMessage);

    await new Promise(r => setTimeout(r, 2000));
  }
  throw new Error('Timeout');
}
Python — Polling
import time

def poll_result(job_id, token, max_attempts=30):
    for _ in range(max_attempts):
        res = requests.get(
            f'https://datawork.dev/api/extract/jobs/{job_id}',
            headers={'Authorization': f'Bearer {token}'}
        )
        data = res.json()
        if data['status'] == 'DONE':   return data
        if data['status'] == 'FAILED': raise Exception(data.get('errorMessage'))
        time.sleep(2)
    raise TimeoutError('Job nicht abgeschlossen')

6. JSON-Ergebnis & Bildausschnitte

Jedes extrahierte Feld enthält: den Wert, einen Konfidenzwert (0–1), die Position im Dokument (Bounding Box, normalisiert 0–1) sowie einen Base64-Bildausschnitt zur visuellen Verifikation.

Beispiel-Antwort — Schweizer Lohnausweis 2024
{
  "jobId": "b7e1-...",
  "status": "DONE",
  "result": {
    "documentType": "GENERIC",
    "sourceFileName": "lohnausweis_2024.pdf",
    "processedPages": 1,
    "analyzedAt": "2024-03-01T10:23:45Z",
    "fields": [
      {
        "key": "arbeitgeber_name",
        "value": "Muster AG",
        "confidence": 0.99,
        "boundingBox": { "page": 1, "x": 0.05, "y": 0.06, "width": 0.35, "height": 0.04, "unit": "normalized" },
        "image": "iVBORw0KGgo..."
      },
      {
        "key": "arbeitnehmer_name",
        "value": "Maria Muster",
        "confidence": 0.98,
        "boundingBox": { "page": 1, "x": 0.05, "y": 0.13, "width": 0.30, "height": 0.04, "unit": "normalized" },
        "image": "iVBORw0KGgo..."
      },
      {
        "key": "ahv_nummer",
        "value": "756.1234.5678.90",
        "confidence": 0.97,
        "boundingBox": { "page": 1, "x": 0.55, "y": 0.06, "width": 0.28, "height": 0.04, "unit": "normalized" }
      },
      {
        "key": "ziffer_1_bruttolohn",
        "value": "95'400.00",
        "confidence": 0.98,
        "boundingBox": { "page": 1, "x": 0.70, "y": 0.28, "width": 0.22, "height": 0.04, "unit": "normalized" },
        "image": "iVBORw0KGgo..."
      },
      {
        "key": "ziffer_9_ahv_iv_eo",
        "value": "5'247.00",
        "confidence": 0.96,
        "boundingBox": { "page": 1, "x": 0.70, "y": 0.54, "width": 0.22, "height": 0.04, "unit": "normalized" }
      },
      {
        "key": "ziffer_10_nettolohn",
        "value": "80'888.00",
        "confidence": 0.98,
        "boundingBox": { "page": 1, "x": 0.70, "y": 0.70, "width": 0.22, "height": 0.04, "unit": "normalized" }
      }
    ]
  }
}
Bildausschnitte — das Feld image enthält einen Base64-String (PNG), direkt in einem <img src="data:image/png;base64,...">-Tag verwendbar. Ideal um den extrahierten Wert visuell gegen das Original zu prüfen, bevor er in die Buchhaltung übernommen wird.

7. Vollständiges Python-Beispiel

Alles in einem Script — Login, Upload, Polling und strukturierte Ausgabe aller Lohnausweis-Felder:

lohnausweis_extraktion.py
import requests
import time

BASE_URL  = 'https://datawork.dev/api'
EMAIL     = 'ihre@email.ch'
PASSWORD  = 'IhrPasswort'
PDF_DATEI = 'lohnausweis_2024.pdf'

FELDER = [
    'arbeitgeber_name', 'arbeitnehmer_name',
    'ahv_nummer', 'beschaeftigungsjahr',
    'ziffer_1_bruttolohn',
    'ziffer_2_verpflegung',
    'ziffer_9_ahv_iv_eo', 'ziffer_9_alv',
    'ziffer_9_pensionskasse', 'ziffer_9_nbuv',
    'ziffer_10_nettolohn',
    'ziffer_13_quellensteuer',
]

# 1. Login
res   = requests.post(f'{BASE_URL}/login', data={'username': EMAIL, 'password': PASSWORD})
token = res.json()['access_token']
headers = {'Authorization': f'Bearer {token}'}
print('✓ Eingeloggt')

# 2. Lohnausweis hochladen
with open(PDF_DATEI, 'rb') as f:
    res = requests.post(
        f'{BASE_URL}/ocr/analyze/async',
        headers=headers,
        files={'file': (PDF_DATEI, f, 'application/pdf')},
        data={'fields': ','.join(FELDER), 'includeImages': 'true'}
    )
job_id = res.json()['jobId']
print(f'✓ Job gestartet: {job_id}')

# 3. Warten bis fertig
print('  Verarbeite...')
for _ in range(30):
    time.sleep(2)
    res  = requests.get(f'{BASE_URL}/ocr/jobs/{job_id}', headers=headers)
    data = res.json()
    if data['status'] == 'DONE':
        print('✓ Fertig!')
        break
    if data['status'] == 'FAILED':
        raise Exception(f"Fehler: {data.get('errorMessage')}")
else:
    raise TimeoutError('Job nicht abgeschlossen')

# 4. Felder ausgeben
print()
print('=' * 55)
print('  LOHNAUSWEIS 2024 — Extrahierte Felder')
print('=' * 55)
for field in data['result']['fields']:
    konfidenz = int(field.get('confidence', 0) * 100)
    wert      = field.get('value', '—')
    key       = field.get('key', '')
    print(f'  {key:<28} {wert:<18} {konfidenz}%')
print('=' * 55)
Ausgabe
✓ Eingeloggt
✓ Job gestartet: b7e1-...
  Verarbeite...
✓ Fertig!

=======================================================
  LOHNAUSWEIS 2024 — Extrahierte Felder
=======================================================
  arbeitgeber_name             Muster AG          99%
  arbeitnehmer_name            Maria Muster       98%
  ahv_nummer                   756.1234.5678.90   97%
  beschaeftigungsjahr          2024               99%
  ziffer_1_bruttolohn          95'400.00          98%
  ziffer_2_verpflegung         0.00               96%
  ziffer_9_ahv_iv_eo           5'247.00           96%
  ziffer_9_alv                 1'145.00           97%
  ziffer_9_pensionskasse       8'120.00           95%
  ziffer_9_nbuv                954.00             94%
  ziffer_10_nettolohn          80'888.00          98%
  ziffer_13_quellensteuer      0.00               99%
=======================================================

Fazit

Mit der datawork.dev API lassen sich Schweizer Lohnausweise vollständig automatisch auslesen — alle Ziffern, Abzüge und Kopffelder in einem einzigen API-Call. Das Ergebnis kommt als strukturiertes JSON zurück, inkl. Konfidenzwerten und Bildausschnitten zur Verifikation.

Ideal für Treuhänder, HR-Software, ERP-Integrationen oder eigene Applikationen — und alle Daten werden ausschliesslich auf Schweizer Servern verarbeitet, konform mit dem Datenschutzgesetz (DSG).