Documentation menu
Getting started
QuickstartCookbook
OverviewNew permit leadsHighest-value permitsFurnace and rewire permitsPermits near a pointMegaproject pipelineProjects near a siteZoning at an addressEverything at one addressEndpoints
GET/zoneGET/geocodeGET/reverse-geocodeGET/zoningGET/sourcesGET/sources/coverageGET/contractor/statsGET/contractor/coverageGET/contractorGET/propertyGET/address/normalizeGET/opening_soonGET/openingGET/businessGET/business/statsGET/business/coverageGET/permit/feedGET/contractor/{name_normalized}GET/permitGET/inspectionGET/licenceGET/development_permitGET/planning_applicationGET/assessmentGET/major_projectGET/permit/statsGET/inspection/statsGET/licence/statsGET/development_permit/statsGET/planning_application/statsGET/assessment/statsGET/major_project/statsGET/permit/coverageGET/inspection/coverageGET/licence/coverageGET/development_permit/coverageGET/planning_application/coverageGET/assessment/coverageGET/major_project/coverageGET/permit/{record_id}/historyGET/permit/{record_id}GET/inspection/{record_id}GET/licence/{record_id}GET/development_permit/{record_id}GET/planning_application/{record_id}GET/assessment/{record_id}GET/major_project/{record_id}Search building permits
Parameters
| Parameter | Type | Description | |
|---|---|---|---|
municipality | string | optional | Filter to one city by its slug, e.g. 'new_westminster', 'toronto', 'north_vancouver_city'. Exact match. This is the city filter for BuildData datasets (permits, development permits, planning applications, inspections); call /{entity_type}/coverage for the list of valid slugs. The 'city' param is accepted as an alias here. (For the US liquor/healthcare/tank datasets, 'city' is instead a distinct filter and 'municipality' does not apply.) |
q | string | optional | Full-text search across the indexed fields of the record, including names (business, establishment, vendor, contractor) wherever the dataset carries them, e.g. q=brewery or q="Iron Hill". This is the name search: there is no separate name= parameter, and it works on every plan including free. It matches the BUSINESS or trade name, the address, city, county and licence/permit number. It does NOT match an owner, licensee or applicant's personal name: these datasets carry public business records only and never store an individual's name, so searching for a person returns nothing. Space-separated words require all terms (e.g. "wood panel"). Use OR to match any term ("wood OR panel OR acoustic"), quotes for exact phrases ("supply arrangement"), and a leading minus to exclude ("software -hardware"). |
permit_type | string | optional | Filter by normalized permit type (exact match): new_construction, renovation, addition, demolition, change_of_use, other. Matches the permit_type_canonical field, not the raw per-city permit_type string. |
trade | string | optional | Filter trade sub-permits by trade (exact match): electrical, plumbing, mechanical, gas, sprinkler, sign, trade_other. A trade permit is pulled for work on one system rather than the building, so this is the field for replacement-cycle work: a furnace swap is trade=mechanical, a rewire is trade=electrical. Only cities that publish trade permits separately carry it; combine with permit_type, which every permit has. |
status | string | optional | Filter by status. Contracts/tenders: Active, Expired, Cancelled, Open (exact). Permits: use a status_canonical value instead (issued, in_review, completed, expired, cancelled, unknown). |
status_canonical | string | optional | Normalized permit status: issued, in_review, completed, expired, cancelled, unknown |
city | string | optional | Filter by city. For the US liquor/healthcare/storage-tank datasets this is a distinct filter (exact, case-insensitive). For all other datasets it is an alias for municipality. |
value_min | number | optional | Minimum value, applied to the entity's value field (construction_value for permits, contract_value for contracts, agreement_value for grants, monetary_amount for contributions, total_co2e for emissions, assessed_value for assessments). Ignored for entity types with no monetary field. |
value_max | number | optional | Maximum value; same per-entity field mapping as value_min. Ignored for entity types with no monetary field. |
issued_after | string | optional | Filter by date >= YYYY-MM-DD (issued_date). Aliases: date_from, date_after |
issued_before | string | optional | Filter by date <= YYYY-MM-DD. Aliases: date_to, date_before |
lat | number | optional | Latitude for proximity search. Send with lng. |
lng | number | optional | Longitude for proximity search. Send with lat. |
radius_km | number | optional | Radius in km, 0.1 to 25. Optional: lat and lng with no radius search 1 km around the point. The radius actually applied is returned as radius_km in the response, so a default is never silent. For every record at one street address rather than everything near it, use /property. |
sort_by | string | optional | Sort field: published (publication_date, tenders only), date (event date; this is award_date for contracts/awards), value (the entity's value field), closing (closing_date for tenders) |
sort_order | string | optional | Sort order: asc or desc |
limit | integer | optional | |
offset | integer | optional | |
cursor | string | optional | Pagination cursor from next_cursor in a previous response. Use instead of offset for deep pagination. |
Request
curl --request GET \
--url 'https://builddata-canadian-construction-data-api.p.rapidapi.com/permit' \
--header 'x-rapidapi-host: builddata-canadian-construction-data-api.p.rapidapi.com' \
--header 'x-rapidapi-key: YOUR_RAPIDAPI_KEY'import requests
url = "https://builddata-canadian-construction-data-api.p.rapidapi.com/permit"
headers = {"x-rapidapi-host": "builddata-canadian-construction-data-api.p.rapidapi.com", "x-rapidapi-key": "YOUR_RAPIDAPI_KEY"}
resp = requests.get(url, headers=headers)
print(resp.json())const res = await fetch("https://builddata-canadian-construction-data-api.p.rapidapi.com/permit", {
headers: {
"x-rapidapi-host": "builddata-canadian-construction-data-api.p.rapidapi.com",
"x-rapidapi-key": "YOUR_RAPIDAPI_KEY",
},
});
const data = await res.json();
console.log(data);<?php
$ch = curl_init("https://builddata-canadian-construction-data-api.p.rapidapi.com/permit");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-rapidapi-host: builddata-canadian-construction-data-api.p.rapidapi.com",
"x-rapidapi-key: YOUR_RAPIDAPI_KEY",
]);
echo curl_exec($ch);require "net/http"
require "uri"
uri = URI("https://builddata-canadian-construction-data-api.p.rapidapi.com/permit")
req = Net::HTTP::Get.new(uri)
req["x-rapidapi-host"] = "builddata-canadian-construction-data-api.p.rapidapi.com"
req["x-rapidapi-key"] = "YOUR_RAPIDAPI_KEY"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.bodypackage main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://builddata-canadian-construction-data-api.p.rapidapi.com/permit", nil)
req.Header.Add("x-rapidapi-host", "builddata-canadian-construction-data-api.p.rapidapi.com")
req.Header.Add("x-rapidapi-key", "YOUR_RAPIDAPI_KEY")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}import java.net.URI;
import java.net.http.*;
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://builddata-canadian-construction-data-api.p.rapidapi.com/permit"))
.header("x-rapidapi-host", "builddata-canadian-construction-data-api.p.rapidapi.com")
.header("x-rapidapi-key", "YOUR_RAPIDAPI_KEY")
.build();
HttpResponse<String> res = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());Example response
{
"entity_type": "permit",
"count": 1,
"offset": 0,
"results": [
{
"record_id": "24 223023 PLB:00",
"municipality": "toronto",
"lat": 43.65401,
"lng": -79.42376,
"work": "Building Permit Related(PS)",
"status": "Permit Issued",
"address": "865 COLLEGE ST",
"description": "Plumbing - INTERIOR ALTERNATION OF 3RD FLOOR SUITE",
"issued_date": "2025-01-29",
"permit_type": "Plumbing(PS)",
"application_date": "2024-10-07",
"construction_value": null
}
]
}Response fields
One permit record. Measured over 6,000 live records sampled across municipalities. Percentages say how often each field is populated: sources publish different columns, so a field missing from a given record is normal rather than an error.
| Field | Type | Description |
|---|---|---|
address | string | Always present. |
application_date | string | Populated in 53% of sampled records. |
area_m2 | number | Populated in 11% of sampled records. |
building_type | string | Populated in 18% of sampled records. |
construction_value | number | Populated in 58% of sampled records. |
contractor | string | Populated in 15% of sampled records. |
contractor_name | string | Populated in 22% of sampled records. |
contractor_name_normalized | string | Populated in 22% of sampled records. |
current_use | string | Populated in 13% of sampled records. |
description | string | Populated in 50% of sampled records. |
dwelling_units_created | string | Populated in 15% of sampled records. |
fetched_at | string | When Nimbus last ingested this record. Bookkeeping, not source data. Always present. |
geocode_failed | boolean | Populated in 67% of sampled records. |
issued_date | string | Populated in 95% of sampled records. |
lat | string | Populated in 75% of sampled records. |
lng | string | Populated in 75% of sampled records. |
municipality | string | City slug. Call /{entity_type}/coverage for every slug in this dataset. Always present. |
neighborhood | string | Populated in 17% of sampled records. |
neighbourhood | string | Populated in 29% of sampled records. |
normalized_address | string | Address normalized for joining the same property across datasets. Always present. |
normalized_business_name | string | Business name normalized for joining the same business across datasets. Populated in 24% of sampled records. |
opening_soon | boolean | Populated in 99% of sampled records. |
permit_number | string | Populated in 97% of sampled records. |
permit_type | string | Populated in 91% of sampled records. |
permit_type_canonical | string | Permit type mapped onto a shared vocabulary. This is what the permit_type filter matches. Always present. |
postal_code | string | Populated in 24% of sampled records. |
province | string | Populated in 93% of sampled records. |
record_id | string | Stable identifier for this record. Pass it to the /{entity_type}/{record_id} endpoint. Always present. |
status | string | Populated in 90% of sampled records. |
status_canonical | string | Status mapped onto a shared vocabulary. This is what the status_canonical filter matches. Always present. |
storeys | integer | Populated in 11% of sampled records. |
structure_type | string | Populated in 21% of sampled records. |
trade_canonical | string | The trade a sub-permit covers (electrical, plumbing, mechanical, gas, sprinkler, sign, trade_other). This is what the trade filter matches. Only cities that publish trade permits separately carry it, so it is absent nationally but present on every permit from those cities. Populated in 2% of sampled records. |
ward | string | Populated in 13% of sampled records. |
work | string | Populated in 85% of sampled records. |
work_type | string | Populated in 16% of sampled records. |
74 more fields published by only one or two cities
Present on under 10% of records. Useful when you are working with a specific city, not something to rely on across the dataset.
| Field | Type | Populated |
|---|---|---|
actual_use | string | 2.4% |
address_details | string | 0.6% |
applicant | string | 3.3% |
applicant_name | string | 7.6% |
application_number | string | 3.3% |
area | integer | 0.6% |
area_ft2 | string | 0.0% |
auc_group | string | 3.0% |
bedrooms | integer | 0.3% |
builder | string | 0.3% |
builder_name | string | 4.3% |
building_category | string | 3.3% |
building_use | string | 6.7% |
category | string | 3.3% |
city | string | 6.7% |
community | string | 9.8% |
completed_date | string | 1.9% |
construction_type | string | 3.3% |
dataset | string | 3.3% |
demolition | boolean | 3.3% |
district | string | 3.3% |
domain | string | 3.3% |
dwelling_units | integer | 8.5% |
dwelling_units_lost | string | 2.6% |
dwellings_created | string | 1.3% |
entered_date | string | 3.3% |
est_const_cost_cents | integer | 3.3% |
existing_gfa_m2 | string | 0.0% |
existing_units | integer | 0.9% |
expiry_date | string | 6.8% |
file_type | string | 3.3% |
floor_area_commercial | integer | 3.3% |
floor_area_industrial | integer | 3.3% |
floor_area_institutional | integer | 3.3% |
floor_area_residential | integer | 3.3% |
footprint_area | number | 3.3% |
former_city | string | 3.3% |
gfa | number | 0.5% |
height | number | 3.3% |
inspection_outcome | string | 0.4% |
land_use | string | 3.1% |
land_use_district | string | 3.1% |
latitude | number | 3.3% |
legal_description | string | 3.2% |
longitude | number | 3.3% |
lots | string | 6.7% |
matricule | string | 3.0% |
most_recent_inspection | string | 0.4% |
net_new_units | integer | 3.3% |
new_floor_area_sqft | number | 0.5% |
occupancy_type | string | 3.3% |
parent_permit_number | string | 0.8% |
permit_cost | number | 1.5% |
permit_fee | number | 3.1% |
permit_group | string | 3.3% |
permit_type_code | string | 3.3% |
permit_type_description | string | 3.3% |
pid | string | 3.3% |
processed_date | string | 0.0% |
proposed_gfa | string | 0.0% |
proposed_use | string | 3.3% |
received_date | string | 3.3% |
revision | string | 3.3% |
second_unit | string | 0.0% |
structure | string | 0.4% |
subdivision | string | 3.3% |
subject | string | 1.2% |
total_units | integer | 4.1% |
units_created | integer | 6.7% |
units_lost | integer | 0.2% |
units_net_change | integer | 3.3% |
work_class | string | 3.3% |
work_scope | string | 3.3% |
zoning | string | 6.7% |
Errors
Every error returns {"detail": "…"}, a sentence naming what was wrong and, where there is one, the fix.
| Status | When |
|---|---|
400 | The request is understood but cannot be served as asked, e.g. offset above 9500, or cursor combined with sort_by=value. The message names the fix. |
403 | Missing or invalid API key. |
404 | No such endpoint, or no record with that id. |
422 | A parameter is invalid: an unknown city slug, an impossible date, a limit above 500, an unrecognised sort field, or a misspelled parameter name. The message names the parameter and, where there is one, the nearest valid value. |
429 | Rate limit or plan quota exceeded. |
500 | Unexpected server error. |
504 | The query took too long. Narrow it with a municipality or a date range. |
Ready to build?Subscribe on RapidAPI to get your key and start calling BuildData in minutes.
Get your API key →