shab-parser
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.
Install
pip install shab-parser
To use the HTTP client (for fetching from the live API):
pip install shab-parser[http]
Quick start
Parse XML you already have
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)
Fetch and parse from the API
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.
Event types
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.
Text extractors
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 — 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 |
Data model
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 | None
API reference
shab_parser.parse(raw: RawResponse) -> Publication
Parse a RawResponse (as returned by ShabClient.fetch()) into a Publication.
shab_parser.parse_xml(xml_bytes, *, source_url="", ref_state=None) -> Publication
Parse raw XML bytes directly. Use this when you already have the XML and don't need the HTTP client.
shab_parser.client.ShabClient
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.
shab_parser.client.parse_bulk_export(xml_bytes) -> (list[PublicationRef], int)
Parse a bulk-export list page into publication references and a total count. Useful if you handle pagination yourself.
shab_parser.extractors
The free-text extractors. See the table above and the documentation.
Background
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.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shab_parser-0.2.0.tar.gz.
File metadata
- Download URL: shab_parser-0.2.0.tar.gz
- Upload date:
- Size: 58.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4318de2c7fbbc4bc761f69f5f0521d7835190d8234abc8bfe1a000cdc1920a46
|
|
| MD5 |
e6e4cbcdcbc9b76960034edaf2dddd95
|
|
| BLAKE2b-256 |
a4833230f6ba7d323fc7c4b73b1391a4237dbf3aa8d65931dccac2bc0b46b7db
|
File details
Details for the file shab_parser-0.2.0-py3-none-any.whl.
File metadata
- Download URL: shab_parser-0.2.0-py3-none-any.whl
- Upload date:
- Size: 67.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6805beb907ecc284d3d5889542ca17d42387eb06e1a80f02052f6ca6e5d0eac0
|
|
| MD5 |
c931ac775b6f202765b72cfd4880433f
|
|
| BLAKE2b-256 |
a9d935b7d77407fa282a0b76e1dfae67997d1d1ba917f6d8183c41377a067cf2
|