An open-source Python package providing Austrian election results through a RESTful API.
- Historical election data (CSV/JSON from data.gv.at)
- Multiple election types (nationalratswahlen, bundespräsidentenwahlen, eu wahlen, landtagswahlen, gemeinderatswahlen)
- Regions: states, federal districts, municipalities
- Parties & lists
- FastAPI backend with OpenAPI schema
- PostgreSQL or SQLite database
- CLI for data ingestion and management (see
typercommands)
A simple JSON-based mapping file can be provided when loading external data. The structure is:
{
"model": "Region", // optional model name to insert
"headers": { // rename columns
"GKZ": "code",
"Gebietsname": "name"
},
"values": { // translate individual values
"type": {"nat":"nationalrat"}
}
}Use the CLI to import a CSV file:
poetry run offenewahlen-api load data.csv --map mapping.jsonIf no mapping is specified the loader will still validate rows and attempt a best-effort model inference.
The project uses one command with multiple actions:
poetry run cli party-registry <action> [args...]Alternative entrypoints:
poetry run offenewahlen-api ...poetry run owat-api cli ...poetry run owat cli ...
Supported actions (for party-registry):
parsediffingestbatch
parse converts one local HTML file to CSV (no network download).
Parse latest local HTML file in data/incoming/party-registry:
poetry run cli party-registry parse data/incoming/party-registry https://citizen.bmi.gv.at/at.gv.bmi.fnsweb-p/par/public/ParteienregisterParse explicit HTML file:
poetry run cli party-registry parse data/incoming/party-registry party-registry.html https://citizen.bmi.gv.at/at.gv.bmi.fnsweb-p/par/public/ParteienregisterCreate a diff between source and target CSV files (default key: parteienregisterzahl):
poetry run cli party-registry diff data/incoming/party-registry/party-registry.csv data/parties/parties.csvDiff outputs are appended to fixed files:
data/incoming/party-registry/party-registry_new-entries.csvdata/incoming/party-registry/party-registry_removed-entries.csv
Rows are only appended if the key (parteienregisterzahl, or the passed key)
is not already present in the respective file.
Ingest parties CSV into the database (default path: data/parties/parties.csv):
poetry run cli party-registry ingestOr with explicit input CSV:
poetry run cli party-registry ingest data/parties/parties.csvRaw HTML persistence is enabled by default for ingest and uses source_html_file
references from the CSV. Missing referenced HTML files are skipped.
Disable raw HTML persistence:
poetry run cli party-registry ingest data/incoming/party-registry/party-registry.csv --skip-rawbatch merges HTML files to one CSV and requires the source URL to be passed:
Batch all HTML files in a directory:
poetry run cli party-registry batch data/incoming/party-registry https://citizen.bmi.gv.at/at.gv.bmi.fnsweb-p/par/public/ParteienregisterBatch only files matching one base filename (<base>.html, <base>_<n>.html):
poetry run cli party-registry batch data/incoming/party-registry 2026-02-27_22-00-00_party-regisry https://citizen.bmi.gv.at/at.gv.bmi.fnsweb-p/par/public/ParteienregisterFor local development, .env uses SQLite:
DATABASE_URL=sqlite+aiosqlite:///./offenewahlen.dbInitialize schema:
poetry run offenewahlen-api init-dbThis project targets Python 3.12 or newer; the pyproject.toml and
container images are already configured accordingly. The source code lives
under src/offenewahlen_api which is added to PYTHONPATH by the various
startup scripts and container configuration. The development
container and Dockerfile both use Python 3.13 as of the latest updates.
Instructions will be added once the MVP is running.
This project is released under the MIT License.