Every Canadian municipality publishes building permit data separately, in its own format. Toronto uses CKAN. Calgary and Edmonton use Socrata. Vancouver uses ArcGIS. Some cities publish Excel files. None agree on field names, date formats, or permit classifications.
BuildData normalizes 3,219,264 permits from 57 cities into a single REST schema, geocodes 81% of records with lat/lng coordinates, and refreshes daily. This is the developer reference: authentication, endpoints, query examples, and design decisions worth knowing before you start.
Every record shares the same schema regardless of source city. The permit_type_canonical field maps local permit names (building permit, construction neuve, renovation, etc.) to six stable categories:
Renovation dominates at 42.3%, followed by new construction at 26.1%. Additions (10.8%) and demolitions (3.1%) are also covered. 81.7% of records are geocoded using Statistics Canada’s Open Database of Addresses.
Top 10 cities by volume:
All endpoints are accessed via RapidAPI. Two headers are required on every request:
# Required on every request
X-RapidAPI-Key: YOUR_API_KEY
X-RapidAPI-Host: builddata-canadian-construction-data-api.p.rapidapi.com
Free tier: 100 requests/day. Pro: 10,000 requests/day at $29/month.
The API has four endpoints. The full OpenAPI 3.0.3 spec is at builddata.ca/openapi.json and imports directly into Postman, Insomnia, or any LLM tool-use framework.
curl -G "https://builddata-canadian-construction-data-api.p.rapidapi.com/permit" \
--data-urlencode "municipality=toronto" \
--data-urlencode "permit_type_canonical=new_construction" \
--data-urlencode "issued_after=2024-01-01" \
--data-urlencode "sort_by=date" \
--data-urlencode "limit=20" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: builddata-canadian-construction-data-api.p.rapidapi.com"
curl -G "https://builddata-canadian-construction-data-api.p.rapidapi.com/permit" \
--data-urlencode "lat=43.6532" \
--data-urlencode "lng=-79.3832" \
--data-urlencode "radius_km=2" \
--data-urlencode "limit=50" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: builddata-canadian-construction-data-api.p.rapidapi.com"
Every list response includes a next_cursor field. Pass it as the cursor parameter on the next request. Cursors are stable across requests, even as new records are added.
import requests
BASE = "https://builddata-canadian-construction-data-api.p.rapidapi.com"
HEADERS = {
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "builddata-canadian-construction-data-api.p.rapidapi.com",
}
params = {"municipality": "calgary", "permit_type_canonical": "new_construction", "limit": 100}
records = []
while True:
r = requests.get(BASE + "/permit", headers=HEADERS, params=params)
data = r.json()
records.extend(data["results"])
if not data.get("next_cursor"):
break
params["cursor"] = data["next_cursor"]
print(f"{len(records)} permits fetched")
curl -G "https://builddata-canadian-construction-data-api.p.rapidapi.com/permit/stats" \
--data-urlencode "municipality=calgary" \
--data-urlencode "group_by=permit_type_canonical" \
--data-urlencode "period=1y" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: builddata-canadian-construction-data-api.p.rapidapi.com"
{
"record_id": "23-123456",
"municipality": "toronto",
"address": "123 QUEEN ST W",
"permit_type": "New Construction",
"permit_type_canonical": "new_construction",
"status": "Issued",
"status_canonical": "issued",
"issued_date": "2024-03-15",
"work": "Erect 12-storey mixed-use building",
"lat": 43.6487,
"lng": -79.3958,
"entity_type": "permit"
}
Canonical vs. raw fields: permit_type_canonical and status_canonical are normalized across all 57 cities. permit_type and status preserve the original municipal values. Use canonical fields for cross-city filters and aggregations; use raw fields when you need the source terminology.
The API does not support offset pagination. Every response returns next_cursor: pass it as the cursor parameter on the next request. Cursors are stable between calls, safe for agents making sequential tool calls across multiple turns.
To answer “how many new construction permits in Calgary last year,” use /permit/stats rather than paging through all records. The stats endpoint returns aggregated counts in under a second:
/permit/stats?municipality=calgary&permit_type_canonical=new_construction&period=1y
lat, lng, and radius_km work without specifying a municipality. Useful for queries near administrative borders, along transit corridors, or in metro areas that span multiple cities.
The q parameter runs full-text search across the work and address fields. Use it to find specific project types (“pool,” “demolition,” a developer name) rather than filtering by canonical type alone.
lat and lng set to null and are returned normally in non-proximity queries.
The short version: 3.2M permits, 57 cities, one schema. No per-city integrations to write, no normalization to build. Start on the free tier, move to Pro when your pipeline is stable.
permit_type_canonical values: renovation (42.3%), new_construction (26.1%), other (14.0%), addition (10.8%), change_of_use (3.7%), demolition (3.1%). Percentages may not sum to exactly 100% due to rounding. Geocoding: Statistics Canada Open Database of Addresses (ODA).