Typed Python client for the Swiss Official Gazette of Commerce (SHAB/SOGC/FUSC).
Fetches publications from the Amtsblattportal public API, parses the XML into dataclasses, and classifies each publication into eleven structured event types: incorporation, branch creation, seat move, address change, rename, purpose change, capital increase, merger, officer change, liquidation, deletion.
Built and maintained by Prospex, a Swiss B2B sales intelligence platform.
pip install shab-parserTo use the HTTP client (for fetching from the live API):
pip install shab-parser[http]from shab_parser import parse_xml
with open("publication.xml", "rb") as f:
pub = parse_xml(f.read())
print(pub.company_name) # "Alpenblick Handel AG"
print(pub.uid) # "CHE-123.456.789"
print(pub.canton) # "ZH"
for event in pub.events:
print(event.event_type, event.effective_date, event.payload)from datetime import date
from shab_parser.client import ShabClient
from shab_parser import parse
with ShabClient() as client:
refs = client.discover(date(2026, 6, 15), date(2026, 6, 15))
for ref in refs[:5]:
raw = client.fetch(ref)
pub = parse(raw)
print(f"{pub.company_name}: {[e.event_type.value for e in pub.events]}")The client rate-limits itself to one request per second and retries transient failures with exponential backoff.
The parser classifies each publication into one or more of eleven events:
| Event | Sub-rubric | Trigger |
|---|---|---|
INCORPORATION |
HR01 | <registration>true</registration> |
BRANCH_CREATED |
HR01 | A branch designation in the publication text |
SEAT_MOVED |
HR02 | Different seat in commonsNew vs. commonsActual |
ADDRESS_CHANGED |
HR02 | <addressChanged>true</addressChanged> |
NAME_CHANGED |
HR02 | Different company name in commonsNew vs. commonsActual |
PURPOSE_CHANGED |
HR02 | Different purpose in commonsNew vs. commonsActual |
CAPITAL_INCREASED |
HR02 | Structured nominal comparison, phrase fallback |
MERGER |
any | A merger clause in the publication text |
OFFICERS_CHANGED |
HR02 | A person block in the text that mutates an office |
LIQUIDATION |
any | Dissolution flags or "in Liquidation" added to name |
DELETED |
HR03 | <delete> block with deletion date |
Events come back in the order above, also available as shab_parser.TAXONOMY_ORDER.
Seven of the eleven come from machine-readable XML fields. Four do not: the register has no flag for a branch creation, a merger or an officer change, and states them only in the publication's prose.
shab_parser.extractors holds the free-text extractors behind those four types. Each
is language-aware across German, French and Italian, returns a plain dict, and works
standalone, which is useful for mining a corpus of publication text where the
structured XML was never kept.
from shab_parser.extractors import extract_merger
text = ("Fusion: reprise des actifs et passifs de First SA, à Nyon "
"(CHE-106.145.608), selon contrat de fusion du 28.11.2025.")
extract_merger(text, "fr")
# {'extractor_version': 'shab-merger/1.0.0',
# 'transactions': [{'direction': 'absorbing',
# 'counterparty_name': 'First SA', ...}]}| Module | Reads |
|---|---|
extractors.branches |
Branch registrations, and the foreign head office behind them |
extractors.mergers |
Mergers, plus FusG asset transfers and demergers |
extractors.persons |
Officers appointed, resigned, or with changed signature rights |
extractors.purposes |
The new statutory purpose |
extractors.renames |
The old and new firm, and the marker that makes it a rename |
parse() and parse_xml() return a Publication dataclass:
@dataclass(frozen=True)
class Publication:
external_id: str
publication_date: date
language: str # "de", "fr", or "it"
source_url: str
company_name: str
raw_text: str
sub_rubric: str # "HR01", "HR02", or "HR03"
effective_date: date | None
canton: str | None
uid: str | None # CHE-xxx.xxx.xxx
legal_form_code: str | None
publication_state: str # "PUBLISHED" or "CANCELLED"
events: list[Event]
company_new: Company | None
company_actual: Company | None
capital_new: float | None
capital_actual: float | NoneParse a RawResponse (as returned by ShabClient.fetch()) into a Publication.
Parse raw XML bytes directly. Use this when you already have the XML and don't need the HTTP client.
HTTP client for the Amtsblattportal API. Requires the http extra.
discover(start, end)lists all HR publications in a date range. Queries bothPUBLISHEDandCANCELLEDstates, deduplicating by external ID.fetch(ref)downloads one publication's full XML.
Parse a bulk-export list page into publication references and a total count. Useful if you handle pagination yourself.
The free-text extractors. See the table above and the documentation.
SHAB (Schweizerisches Handelsamtsblatt) is the official gazette where Swiss commercial
register entries are published. Every new company, every seat change, every capital
increase, every deletion passes through it. The same publication appears in German, French,
and Italian, each under a different namespace (HR01:, HR02:, HR03:), but with
identical XML structure.
This library handles the namespace differences transparently using ElementPath's {*}
wildcard, so you get the same parsed output regardless of language.
MIT