← Home

How this site is built

Every chart on this site reads from small, precomputed JSON files, never a live database. Here's how those files get made.

Where the data comes from

1996 to 2020 comes from data.ontario.ca as per-year CSV resources. 2021 onward comes directly from each year's page under ontario.ca/public-sector-salary-disclosure. Both sources publish the same five columns for every disclosed employee: sector, name, salary paid, taxable benefits, employer, job title, and the calendar year. Inflation adjustment uses Statistics Canada's Consumer Price Index series (table 18-10-0005-01).

From 600MB of CSV to a few megabytes of JSON

All 30 years load into a local DuckDB database and collapse into a normalized star schema: one table of roughly 5 million salary rows, plus small lookup tables for employers and job titles. Every chart on this site is a precomputed aggregate exported from that schema, a median by sector by year, a histogram bin, a top-earners list, never a query against raw rows at request time. That's a deliberate split: charts need aggregates and must stay fast and free to host; the separate name search on this site (see the employees page) is the only part of this project that touches a real database, because it's a genuinely different problem with genuinely different scaling needs.

The hard part: the same employer, spelled differently every year

Employer names drift from year to year and file to file. "Toronto, City of", "City Of Toronto", and "TORONTO (CITY)" are the same institution, but nothing in the raw data says so. Sector names have been renamed and merged over 30 years. Some records are in French. Name casing is inconsistent throughout.

Reconciling that is a fuzzy-match pass followed by a hand-maintained override table (etl/overrides/employers.csv in the source repository) that resolves every case the automated pass gets wrong. There's no shortcut for this part: it's the single most time-consuming step in the pipeline, and it's what makes an "employer" page on this site mean one real institution across 30 years instead of a dozen near-duplicate slivers of one.

What's public, and what isn't indexed

This data is public record by statute. That doesn't mean every page on this site should be a fast, permanent Google result for someone's name: the search and individual person pages carry a noindex tag, so this site doesn't become a name-lookup index in search results even though the underlying data is fully public and searchable directly on this site.

Source

The full pipeline, from raw CSV to the JSON files these charts read, is open source: github.com/payamyek/ontario-sunshine-list.