A headless REST API built with Laravel 12 that exposes historical electoral data for the election results of the city of Fribourg from 1996 to 2026. It covers both legislative (Conseil général) and executive (Conseil communal) elections, including seat allocations, detailed panachage results, and candidate career tracking.
🖥️ Frontend: The companion web application is available at github.com/nicolasfeyer/cg2r.
- Election listings – Browse all elections by power type (legislative / executive)
- Seat allocation – Results computed using the official methods:
- Hare quota (elections ≤ 2001)
- Hagenbach-Bischoff quota (elections > 2001)
- Panachage details – Cross-list vote transfers per candidate and per list
- Statistical analyses – Trade balance, external support, seats & candidates evolution over time
- People tracking – Search candidates, view career histories, detect political weathercocks
- OpenAPI / Swagger UI – Interactive API documentation at
/api/documentation
- PHP 8.2+
- Composer
- MySQL 5.7+ (or MariaDB 10.3+)
# 1. Clone the repository
git clone https://github.com/YOUR_USERNAME/elections-api.git
cd elections-api
# 2. Install PHP dependencies
composer install
# 3. Set up your environment
cp .env.example .env
php artisan key:generate
# 4. Configure your database in .env, then import the schema
mysql -u root -p your_database < schema.sql
# 5. Request the dataset from the author and import it (see "Database" section below)
# 6. Start the development server
php artisan serveThe API will be available at http://localhost:8000/api.
Once the server is running, navigate to:
http://localhost:8000/api/documentation
To regenerate the OpenAPI spec after code changes:
php artisan l5-swagger:generateAll routes are prefixed with /api.
| Method | Endpoint | Description |
|---|---|---|
GET |
/elections/{power} |
List all election years for a power type |
GET |
/elections/{power}/{year}/summary |
Seat summary for an election |
GET |
/elections/{power}/{year}/lists |
All lists (parties) in an election |
GET |
/elections/{power}/{year}/{list_no}/results |
Detailed candidate results for a list |
GET |
/elections/{power}/{year}/voting-details |
Detailed voting breakdown per list |
GET |
/elections/{power}/{year}/external-details |
Cross-list vote support data |
GET |
/elections/{power}/{year}/trade-balance |
Vote trade balance per list |
| Method | Endpoint | Description |
|---|---|---|
GET |
/elections/{power}/seats-evolution |
Seat count evolution across elections |
GET |
/elections/{power}/candidates-evolution |
Elected candidates evolution |
GET |
/elections/{power}/lists-meta |
Metadata for all lists of a power |
GET |
/elections/parties-meta |
All parties with historical names and colors |
| Method | Endpoint | Description |
|---|---|---|
GET |
/elections/candidacy/{power}/{year}/scatter |
Candidate scatter data for a year |
GET |
/elections/candidacy/{power}/scatter |
Candidate scatter data for all years |
GET |
/elections/candidacy/search?q={term} |
Search candidates by name or profession |
GET |
/elections/person/results?id={id} |
Full career results for a person |
GET |
/elections/person/weathercock |
People who changed party affiliation |
{power} accepts: legislative or executive
The schema is available in schema.sql and covers the following tables:
elections– Election eventslists– Party lists per electioncandidacies– Candidates per listpeople– Persons (linked across elections)modified_ballot_results– Panachage vote countslist_party_affiliation– List ↔ party linksparties+party_history– Party identities over time
⚠️ Data not included – Only the database schema is provided in this repository. To obtain the actual dataset, please contact the author to request access to the data.
Key environment variables (see .env.example):
| Variable | Description | Default |
|---|---|---|
DB_HOST |
Database host | 127.0.0.1 |
DB_DATABASE |
Database name | elections |
DB_USERNAME |
Database user | root |
DB_PASSWORD |
Database password | (empty) |
L5_SWAGGER_GENERATE_ALWAYS |
Auto-generate docs on every request | false |
Contributions are welcome for non-commercial purposes. Please open an issue first to discuss what you would like to change.
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International license. See the LICENSE file for details.
You are free to: share and adapt this material for non-commercial purposes, as long as you give appropriate credit.