Nine permit desks
Every city on the page at once — you don't have to pick one to see anything. Each row is a city's entire permit record; click a row to open it below. The capability chips say what that city's data can and can't support, because no two of these schemas are alike.
The whole record
Where the building actually happens
Every geocoded permit binned to a fixed 0.004° cell — roughly 445 m north-south — and counted by the year it was issued. Drag the year slider to watch activity move across the city.
What's being permitted
Each city classifies its own permits, so these categories are the city's words, not a shared national scheme — read them within a city, never across two.
Who builds the city
The firms that file the most permits. Only names on 20 or more permits appear — a deliberate threshold, not a data limit: these fields routinely carry a private individual's name on their own renovation permit, and a name set in large type is more exposed than a row in a table.
How long it takes
Two intervals a city's own records can measure that no national statistic can: how long a permit waits for approval, and how long the building then takes. Medians, not means — both distributions have long right tails.
What this data can't tell you
Every card above is one city's administrative record, published as-is. These are the limits that matter before you quote any of it.
How this page was built
Two versions of the same explanation.
In plain language
When someone wants to build, renovate or demolish, they need a permit from the city. Cities publish those permits as open data — every one, with a date, a location, what the work is, and usually what it's expected to cost.
We download nine cities' permit records and count them up in a few different ways:
- The record over time — how many permits a city issued each month, going back as far as that city publishes. Some go back to the 1990s; one only starts in late 2020.
- The map — we lay a grid of roughly 445-metre squares over the city and count the permits in each square, year by year. We do not show individual permits: putting somebody's home renovation on a public map by address isn't something the data being open makes okay.
- Who builds — some cities record the firm that filed each permit. We show the ones that appear on 20 or more permits, so a homeowner who pulled a single permit never shows up.
- How long it takes — where a city records both an application date and an issue date, we can measure the wait. Where it also records a completion date, we can measure the build.
The most important thing to know: a city is not its metro area. The City of Toronto is well under half the Toronto region. These numbers can't be added to, or compared with, the metro figures on the Construction Tracker.
For the technical reader
Sources. Nine municipal open-data endpoints on five different platforms: Opendatasoft Explore v2.1 (Vancouver), CKAN datastore_search (Toronto) and datastore_search_sql (Montreal), Socrata SODA/SoQL (Calgary, Edmonton, Winnipeg), Esri ArcGIS FeatureServer (Mississauga, Halifax), and 15 annual XLSX workbooks with no API at all (Ottawa). Each city's licence is stated in its row above.
Aggregation. Where the platform supports it, cuts are computed server-side (SoQL date_trunc_ym/date_trunc_y with two-dimensional $group; PostgreSQL round(x::numeric,3) and percentile_cont on Montreal's SQL endpoint). Socrata has no round() function — verified live, it returns 400 no-such-function — so coordinate binning is done client-side in Python after paging raw rows ordered by :id.
The grid. Cells are 0.004°, indexed as integers against that step; a cell holds a permit count and, where the city has a cost field, a dollar total. Nothing else. Cell size was chosen by measurement: on Calgary (498,889 geocoded rows, 28 years) 0.002° produced 156,695 cell-year rows / 3.04 MB against 81,454 / 1.61 MB at 0.004°. Colour breaks are quantiles of the displayed year's non-empty cells, printed in the legend.
Category matrices. Each cut keeps the top 14 labels by all-time weight and rolls the remainder into an explicit Other (N categories) row, so a year column still sums to the city's true total. Labels are each city's own raw field values, normalised only for casing where a city carries genuine duplicates (Montreal's description_type_batiment).
Weighting basis. Montreal and Winnipeg publish no cost field at all, so everything there is weighted by permit count and the page says so per card rather than showing an empty dollar axis. Toronto's EST_CONST_COST carries a literal placeholder string on roughly half its rows; those are counted and excluded, never coerced to zero.
Pipeline. Python/permits_detail_etl.py → permits_json/; the carried-over processing/build-time and unit-economics panels come from Python/municipal_permits_etl.py → construction_json/municipal.json, which stays the single producer for those. Full writeup: docs/PERMITS.md.