API v1

API-Dokumentation

Die Firmenwatch-API liefert die Handelsregister- und Konkurspublikationen des Schweizerischen Handelsamtsblatts in normalisierter Form: Firmensuche, Einzelabfrage per UID, Organe, Historie, Ereignisstrom und UID-Prüfung. Die Schnittstelle ist eine REST-API mit JSON-Antworten, sie setzt keine besondere Bibliothek voraus.

Schnellstart

Alle Pfade liegen unter https://firmenwatch.ch/api/v1. Ausser /health trägt jede Anfrage den API-Schlüssel im Authorization-Kopf. Die folgende Abfrage liefert aktive Aktiengesellschaften im Kanton Zürich, nach der letzten Änderung sortiert.

Anfrage
curl -s "https://firmenwatch.ch/api/v1/companies?canton=ZH&legal_form=0106&limit=2" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Antwort
{
  "data": [
    {
      "uid": "CHE-000.000.000",
      "name": "Muster Handels AG",
      "slug": "muster-handels-ag-zuerich",
      "legal_form_code": "0106",
      "legal_form": "Aktiengesellschaft",
      "status": "active",
      "seat": "Zürich",
      "canton": "ZH",
      "address": {
        "care_of": null,
        "street": "Musterstrasse",
        "house_number": "12",
        "po_box": null,
        "zip": "8001",
        "town": "Zürich"
      },
      "purpose": "Handel mit Waren aller Art, insbesondere mit Büromaterial.",
      "capital": { "nominal": 250000, "paid": 250000, "currency": "CHF" },
      "industry": { "code": "46.90", "label": "Grosshandel ohne ausgeprägten Schwerpunkt" },
      "first_registered_at": "2019-03-14",
      "deleted_at": null,
      "last_event_at": "2026-08-24",
      "url": "https://firmenwatch.ch/firma/muster-handels-ag-zuerich"
    }
  ],
  "total": 1,
  "limit": 2,
  "offset": 0
}

Alle Beispieldaten dieser Seite sind erfunden. Die UID CHE-000.000.000 bezeichnet keine eingetragene Rechtseinheit.

Authentifizierung

Die Authentifizierung erfolgt über einen Bearer-Token. Produktivschlüssel beginnen mit fw_live_. Es wird nur der Hash gespeichert; geht ein Schlüssel verloren, erstellen Sie im Konto einen neuen und widerrufen den alten. Der Schlüssel gehört ausschliesslich auf den Server: er trägt die Rechte des Kontos und darf nicht in Browser-Code, Mobil-Apps oder öffentliche Repositorien gelangen. Ein kompromittierter Schlüssel wird im Konto widerrufen und ersetzt.

Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx

Fehlt der Kopf oder ist der Schlüssel unbekannt, antwortet die API mit HTTP 401 und dem Code unauthorized. Anfragen über unverschlüsseltes HTTP werden abgewiesen.

Konventionen

  • Datum und Zeit. Datumsangaben folgen ISO 8601 im Format 2026-08-26. Zeitstempel sind UTC.
  • UID. Entgegengenommen werdenCHE-000.000.000, CHE000000000 und 000000000. Zurückgegeben wird immer die kanonische Schreibweise mit Bindestrich und Punkten.
  • Listen. Mehrwertige Filter im Ereignisstrom werden kommagetrennt übergeben, etwa cantons=ZH,ZG,AG. Die Werte sind oder-verknüpft, verschiedene Parameter und-verknüpft.
  • Blätterung. limit undoffset steuern die Seiten. Bei der Firmensuche nennt totaldie Gesamtzahl der Treffer. Für laufende Ereignis-Abholungen istsince_id dem Versatz vorzuziehen.
  • Leere Felder. Nicht erhobene Werte sindnull, nicht der leere String. Bedingte Felder wienext_since_id werden nur geliefert, wenn sie einen Wert haben.
  • Zeichensatz. Antworten sind UTF-8. Umlaute und Akzente werden unverändert aus der Publikation übernommen.

Endpunkte

Sieben Endpunkte decken den gesamten Funktionsumfang ab. Alle Pfade sind relativ zu https://firmenwatch.ch/api/v1.

GET/companies

Firmensuche

Volltext- und Facettensuche über alle eingetragenen Rechtseinheiten. Ohne Suchbegriff stehen aktive, zuletzt veränderte Einträge zuoberst.

Parameter
NameTypPflichtBedeutung
qstringSuchbegriff. Trifft Firmenname oder UID.
cantonstringEin Kantonskürzel, z. B. ZH.
legal_formstringEin Rechtsformcode nach eCH-0097. 0106 ist die AG, 0107 die GmbH.
zipstringEine Postleitzahl des Domizils.
statusstringactive, in_liquidation, bankrupt oder deleted.
limitintegerDatensätze pro Seite. 1 bis 100, Vorgabe 25.
offsetintegerVersatz für die Blätterung. 0 bis 100 000, Vorgabe 0.
Anfrage
curl -s -G "https://firmenwatch.ch/api/v1/companies" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  --data-urlencode "q=Muster Handels" \
  --data-urlencode "canton=ZH" \
  --data-urlencode "status=active" \
  --data-urlencode "limit=25"
Antwort (gekürzt)
{
  "data": [
    {
      "uid": "CHE-000.000.000",
      "name": "Muster Handels AG",
      "slug": "muster-handels-ag-zuerich",
      "legal_form_code": "0106",
      "legal_form": "Aktiengesellschaft",
      "status": "active",
      "seat": "Zürich",
      "canton": "ZH",
      "capital": { "nominal": 250000, "paid": 250000, "currency": "CHF" },
      "last_event_at": "2026-08-24",
      "url": "https://firmenwatch.ch/firma/muster-handels-ag-zuerich"
    }
  ],
  "total": 1,
  "limit": 25,
  "offset": 0
}
GET/companies/:uid

Einzelabfrage per UID

Stammdaten einer Rechtseinheit samt bekannten Organen und den 50 neuesten Ereignissen. Der Pfadparameter nimmt jede übliche UID-Schreibweise entgegen.

Pfadparameter
NameTypPflichtBedeutung
uidstringjaCHE-000.000.000 oder 000000000.
Anfrage
curl -s "https://firmenwatch.ch/api/v1/companies/CHE-000.000.000" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Antwort (gekürzt)
{
  "data": {
    "uid": "CHE-000.000.000",
    "name": "Muster Handels AG",
    "slug": "muster-handels-ag-zuerich",
    "legal_form_code": "0106",
    "legal_form": "Aktiengesellschaft",
    "status": "active",
    "seat": "Zürich",
    "canton": "ZH",
    "address": {
      "care_of": "c/o Muster Treuhand AG",
      "street": "Musterstrasse",
      "house_number": "12",
      "po_box": null,
      "zip": "8001",
      "town": "Zürich"
    },
    "purpose": "Handel mit Waren aller Art, insbesondere mit Büromaterial.",
    "capital": { "nominal": 250000, "paid": 250000, "currency": "CHF" },
    "industry": { "code": "46.90", "label": "Grosshandel ohne ausgeprägten Schwerpunkt" },
    "first_registered_at": "2019-03-14",
    "deleted_at": null,
    "last_event_at": "2026-08-24",
    "url": "https://firmenwatch.ch/firma/muster-handels-ag-zuerich",
    "officers": [
      {
        "name": "Muster, Anna",
        "kind": "person",
        "first_name": "Anna",
        "last_name": "Muster",
        "place_of_origin": "Bern",
        "function": "Mitglied des Verwaltungsrates",
        "role": "director",
        "signature": "Einzelunterschrift",
        "active": true,
        "from": "2019-03-14",
        "to": null
      }
    ],
    "timeline": [
      {
        "id": 4812993,
        "type": "address_change",
        "occurred_at": "2026-08-24",
        "canton": "ZH",
        "summary": "Neues Domizil: Musterstrasse 12, 8001 Zürich.",
        "severity": 40,
        "payload": {
          "field": "address",
          "after": "Musterstrasse 12, 8001 Zürich"
        }
      }
    ]
  }
}
GET/companies/:uid/officers

Organe

Die eingetragenen Personen mit Funktion und Zeichnungsberechtigung. Standardmässig nur amtierende Organe.

Parameter
NameTypPflichtBedeutung
uidstringjaPfadparameter. UID der Rechtseinheit.
include_formerbooleanAusgeschiedene Organe mitliefern. Vorgabe false.
Anfrage
curl -s "https://firmenwatch.ch/api/v1/companies/CHE-000.000.000/officers?include_former=true" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Antwort
{
  "data": [
    {
      "name": "Muster, Anna",
      "kind": "person",
      "first_name": "Anna",
      "last_name": "Muster",
      "place_of_origin": "Bern",
      "function": "Mitglied des Verwaltungsrates",
      "role": "director",
      "signature": "Einzelunterschrift",
      "active": true,
      "from": "2019-03-14",
      "to": null
    },
    {
      "name": "Beispiel, Peter",
      "kind": "person",
      "first_name": "Peter",
      "last_name": "Beispiel",
      "place_of_origin": "Basel",
      "function": "Geschäftsführer",
      "role": "managing_officer",
      "signature": "Kollektivunterschrift zu zweien",
      "active": false,
      "from": "2019-03-14",
      "to": "2024-11-06"
    }
  ],
  "uid": "CHE-000.000.000"
}
GET/companies/:uid/events

Historie

Die neuesten normalisierten Ereignisse zu einer UID, neueste zuerst.

Parameter
NameTypPflichtBedeutung
uidstringjaPfadparameter. UID der Rechtseinheit.
limitintegerDatensätze. 1 bis 200, Vorgabe 50.
Anfrage
curl -s "https://firmenwatch.ch/api/v1/companies/CHE-000.000.000/events?limit=50" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Antwort
{
  "data": [
    {
      "id": 4790112,
      "type": "officer_removed",
      "occurred_at": "2024-11-06",
      "canton": "ZH",
      "summary": "Beispiel, Peter, Geschäftsführer, ist ausgeschieden.",
      "severity": 50,
      "payload": {
        "persons": [{ "name": "Beispiel, Peter", "role": "managing_officer" }]
      }
    }
  ],
  "uid": "CHE-000.000.000"
}
GET/events

Ereignisstrom

Der chronologische Strom aller Publikationen. Grundlage für Lead-Feeds und Portfolioüberwachung.

Parameter
NameTypPflichtBedeutung
typesstringEreignistypen, kommagetrennt, z. B. new_registration,bankruptcy_opened.
cantonsstringKantonskürzel, kommagetrennt.
zipsstringPostleitzahlen, kommagetrennt.
legal_formsstringRechtsformcodes nach eCH-0097, kommagetrennt.
industriesstringNOGA-Codes, kommagetrennt.
keywordsstringStichwörter in Firmenname oder Zweck, kommagetrennt.
exclude_keywordsstringAuszuschliessende Stichwörter, kommagetrennt.
capital_minnumberMindestkapital.
capital_maxnumberHöchstkapital.
fromdateFrühestes Publikationsdatum, einschliesslich.
todateSpätestes Publikationsdatum, einschliesslich.
since_idintegerNur Ereignisse mit höherer ID. Empfohlen für die laufende Abholung.
min_severityintegerMindest-Relevanz von 0 bis 100.
limitintegerDatensätze. 1 bis 1 000, Vorgabe 100.
offsetintegerVersatz ohne since_id; im Cursor-Modus ignoriert.

Mit since_id lässt sich der Strom lückenlos abholen: die höchste bereits verarbeitete ID mitgeben, das Feld next_since_id der Antwort speichern und beim nächsten Abruf verwenden. Eine Blätterung über Datum und Versatz verliert dagegen Ereignisse, sobald an einem Tag mehrere Publikationen nachträglich eintreffen.

Anfrage
curl -s -G "https://firmenwatch.ch/api/v1/events" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  --data-urlencode "types=new_registration" \
  --data-urlencode "cantons=ZH,ZG" \
  --data-urlencode "since_id=4812993" \
  --data-urlencode "limit=100"
Antwort (gekürzt)
{
  "data": [
    {
      "id": 4813004,
      "type": "new_registration",
      "occurred_at": "2026-08-25",
      "summary": "Neueintragung: Muster Handels AG",
      "severity": 60,
      "payload": {
        "legalForm": "Aktiengesellschaft",
        "capital": "250000.00"
      },
      "company": {
        "uid": "CHE-000.000.000",
        "name": "Muster Handels AG",
        "legal_form": "Aktiengesellschaft",
        "canton": "ZH",
        "address": {
          "care_of": null,
          "street": "Musterstrasse",
          "house_number": "12",
          "zip": "8001",
          "town": "Zürich"
        },
        "purpose": "Handel mit Waren aller Art.",
        "capital": 250000,
        "industry": "Grosshandel ohne ausgeprägten Schwerpunkt",
        "url": "https://firmenwatch.ch/firma/muster-handels-ag-zuerich"
      }
    }
  ],
  "limit": 100,
  "next_since_id": 4813004
}
POST/uid/validate

UID prüfen und normalisieren

Prüft Format und Prüfziffer nach der Spezifikation des Bundesamts für Statistik (Modulo 11 mit den Gewichten 5, 4, 3, 2, 7, 6, 5, 4) und schlägt die UID anschliessend im Bestand nach.

Rumpf (application/json)
NameTypPflichtBedeutung
uidstringjaUID in beliebiger Schreibweise, mit oder ohne Präfix CHE.
Felder unter data
NameTypPflichtBedeutung
validbooleanFormat und Prüfziffer sind korrekt.
normalizedstring | nullKanonische Schreibweise, null bei ungültiger UID.
foundbooleanDie UID ist im Firmenwatch-Bestand vorhanden.
companyCompany | nullStammdaten, sofern gefunden.

Eine formal gültige UID muss nicht eingetragen sein: valid undfound sind bewusst getrennt. Für eine Onboarding-Prüfung ist in der Regel beides zu verlangen.

Anfrage
curl -s -X POST "https://firmenwatch.ch/api/v1/uid/validate" \
  -H "Authorization: Bearer fw_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"uid":"CHE-000.000.000"}'
Antwort
{
  "data": {
    "valid": true,
    "normalized": "CHE-000.000.000",
    "found": false,
    "company": null
  }
}
GET/health

Betriebszustand

Zeigt, bis zu welchem SHAB-Publikationsdatum der Bestand nachgeführt ist. Geeignet für die eigene Überwachung.

Dieser Endpunkt ist öffentlich und benötigt keinen API-Schlüssel. Bei veralteten Daten oder nicht erreichbarer Datenbank antwortet er mit HTTP 503.

Anfrage
curl -s "https://firmenwatch.ch/api/v1/health"
Antwort
{
  "ok": true,
  "latest_publication": "2026-08-25",
  "data_age_days": 1,
  "entities": 0,
  "events": 0,
  "publications": 0
}

Die Zahlen im Beispiel sind Platzhalter. Massgebend ist der Wert, den die Schnittstelle zum Abfragezeitpunkt zurückgibt.

Datenmodell

Company, Officer und zwei schlanke Ereignisvarianten tragen die Schnittstelle. Die vollständigen Typen stehen in der OpenAPI-Beschreibung.

Company

Felder
NameTypPflichtBedeutung
uidstringjaKanonische UID, z. B. CHE-000.000.000.
namestringjaFirmenwortlaut gemäss Handelsregister.
slugstringjaBezeichner des öffentlichen Profils unter /firma/.
legal_form_codestring | nullRechtsformcode nach eCH-0097.
legal_formstring | nullDeutsche Bezeichnung der Rechtsform.
statusstringjaactive, in_liquidation, bankrupt oder deleted.
seatstring | nullPolitische Gemeinde des Sitzes.
cantonstring | nulljaKantonskürzel, sofern vorhanden.
addressobjectcare_of, street, house_number, po_box, zip, town.
purposestring | nullZweckumschreibung.
capitalobjectnominal, paid, currency.
industryobject | nullcode und label nach NOGA, sofern klassifiziert.
first_registered_atdate | nullDatum der Ersteintragung.
deleted_atdate | nullDatum der Löschung, sofern erfolgt.
last_event_atdate | nullPublikationsdatum des jüngsten Ereignisses.
urlstringÖffentliches Firmenprofil auf firmenwatch.ch.

Officer

Felder
NameTypPflichtBedeutung
namestringjaNachname, Vorname wie publiziert.
kindstringjaperson oder legal_entity.
first_namestring | nullVorname, sofern trennbar.
last_namestring | nullNachname, sofern trennbar.
place_of_originstring | nullAmtlich publizierter Heimatort.
functionstring | nullFunktion, z. B. Mitglied des Verwaltungsrates.
rolestring | nullNormalisierte Rolle.
signaturestring | nullZeichnungsberechtigung im Wortlaut der Publikation.
activebooleanjaOb das Organ aktuell eingetragen ist.
fromdate | nullPublikationsdatum des Eintritts.
todate | nullPublikationsdatum des Austritts, null bei amtierendem Organ.

Event

Felder
NameTypPflichtBedeutung
idintegerjaMonoton steigend, geeignet für since_id.
typestringjaNormalisierter Ereignistyp, siehe Liste unten.
occurred_atdatejaSHAB-Publikationsdatum.
cantonstring | nullKanton der publizierenden Stelle.
summarystring | nullEine Zeile Klartext zum Ereignis.
severityintegerRelevanz von 0 bis 100. Konkurs und Löschung liegen oben.
payloadJSON | nullStrukturierte Details des Ereignistyps.

FeedEvent

Der globale Ereignisstrom liefert dieselben Kernfelder, ergänzt umcompany mit UID, Firmenname, Rechtsform, Kanton, Adresse, Zweck, Nominalkapital, Branche und Profil-URL. Der Kanton steht dort im Firmenobjekt.

Ereignistypen

new_registration      name_change           address_change
seat_change           purpose_change        capital_change
legal_form_change     officer_added         officer_removed
auditor_change        liquidation_opened    liquidation_closed
deletion              bankruptcy_opened     bankruptcy_suspended
bankruptcy_closed     moratorium            debt_enforcement
branch_opened         branch_closed         merger
other

Fehlerreferenz

Fehler tragen stets denselben Rumpf. Der HTTP-Status nennt die Klasse, das Feld code den genauen Grund. message ist für Menschen bestimmt und kann sich ändern; Programme werten ausschliesslich code aus.

{
  "error": {
    "code": "invalid_request",
    "message": "Der Parameter limit muss zwischen 1 und 100 liegen.",
    "details": { "parameter": "limit", "value": "500" }
  }
}
CodeHTTPAnlass
unauthorized401Kein Authorization-Kopf, unbekannter oder widerrufener Schlüssel.
forbidden403Der Schlüssel ist gültig, das Abonnement aber nicht aktiv.
not_found404Die angefragte UID ist nicht im sichtbaren Bestand.
invalid_request400Pflichtfeld fehlt, UID ist ungültig oder der JSON-Rumpf ist fehlerhaft.
rate_limited429Zu viele Anfragen im laufenden Minutenfenster.
quota_exceeded402Das Monatskontingent des Tarifs ist ausgeschöpft.
internal500Unerwarteter Fehler auf unserer Seite. Wiederholung nach kurzer Wartezeit ist zulässig.

Ratenbegrenzung

Jede authentifizierte Antwort trägt den Stand des laufenden Minutenfensters in drei Kopfzeilen. Wird das Fenster überschritten, antwortet die API mit HTTP 429 und dem Code rate_limited.

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1787654400
Kopfzeilen
NameTypPflichtBedeutung
X-RateLimit-LimitintegerZulässige Anfragen im laufenden Fenster.
X-RateLimit-RemainingintegerIm laufenden Fenster verbleibende Anfragen.
X-RateLimit-ResetintegerUnix-Zeit in Sekunden, zu der das Fenster zurücksetzt.

Empfohlen ist ein Wiederholungsversuch mit exponentiell wachsender Wartezeit, beginnend bei einer Sekunde. Wer den Ereignisstrom abholt, fährt mit einem Abruf alle fünf Minuten und since_id besser als mit häufigem Blättern.

Kontingente je Tarif

Das Monatskontingent zählt beantwortete Anfragen. Ist es ausgeschöpft, antwortet die API mit HTTP 402 und dem Code quota_exceeded, bis der nächste Abrechnungsmonat beginnt oder der Tarif gewechselt wird.

Tarif
Gratis
Preis
CHF 0/Monat
Kontingent
200 Abfragen im Monat
Rate
20 pro Minute
Überschreitung
gesperrt statt verrechnet
Tarif
Start
Preis
CHF 29/Monat
Kontingent
1'000 Abfragen im Monat
Rate
40 pro Minute
Überschreitung
gesperrt statt verrechnet
Tarif
Signal
Preis
CHF 79/Monat
Kontingent
5'000 Abfragen im Monat
Rate
60 pro Minute
Überschreitung
gesperrt statt verrechnet
Tarif
Monitor
Preis
CHF 199/Monat
Kontingent
25'000 Abfragen im Monat
Rate
180 pro Minute
Überschreitung
gesperrt statt verrechnet
Tarif
Business
Preis
CHF 449/Monat
Kontingent
150'000 Abfragen im Monat
Rate
600 pro Minute
Überschreitung
gesperrt statt verrechnet

Verbindlich sind die Werte im Konto unter /app/api; dort steht auch der laufende Verbrauch. Jede Antwort trägt ausserdem die Kopfzeilen X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset — damit lässt sich die Begrenzung im eigenen System auslesen, statt sie fest zu verdrahten.

Versionierung

Die Version steht im Pfad. Innerhalb von v1 kommen ausschliesslich Felder und Endpunkte hinzu; bestehende Felder werden weder umbenannt noch entfernt. Clients müssen unbekannte Felder tolerieren. Ein Bruch dieser Zusage erfolgt nur über einen neuen Pfad.

Die maschinenlesbare Beschreibung liegt unter /api/v1/openapi.json und ist die massgebende Quelle für SDK-Generatoren und Testwerkzeuge.

curl -s https://firmenwatch.ch/api/v1/openapi.json -o firmenwatch-openapi.json