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 business licences
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"). |
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). |
is_active | boolean | optional | Business licences: true returns only currently active licences, false only inactive ones. Most licence records are historical, so this is usually what you want. |
is_opening | boolean | optional | Business licences: true returns only business openings, meaning the first licence this business ever held in this city. Renewals reuse the name and are excluded. Pair with opened_from to get the businesses that opened in a period. |
preceded_by_permit | boolean | optional | Business licences: true returns only openings that had a building permit at the same address in the two years before they opened, meaning the fit-out is on record. false returns openings that took space needing no work. |
opened_from | string | optional | Business licences: earliest opening date, YYYY-MM-DD. Filters on when the business was first licensed here, not on when this particular licence was issued. |
opened_to | string | optional | Business licences: latest opening date, YYYY-MM-DD. |
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. |
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/licence' \
--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/licence"
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/licence", {
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/licence");
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/licence")
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/licence", 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/licence"))
.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 licence record. Measured over 5,446 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 | Populated in 89% of sampled records. |
business_name | string | Populated in 97% of sampled records. |
category | string | Populated in 99% of sampled records. |
category_canonical | string | Licence category mapped onto a shared vocabulary. Always present. |
city | string | Always present. |
expiry_date | string | Populated in 34% of sampled records. |
fetched_at | string | When Nimbus last ingested this record. Bookkeeping, not source data. Always present. |
first_licensed_at | string | Populated in 95% of sampled records. |
geocode_failed | boolean | Populated in 14% of sampled records. |
is_active | boolean | Always present. |
is_contractor | boolean | Always present. |
issued_date | string | Populated in 67% of sampled records. |
last_updated | string | Populated in 20% of sampled records. |
lat | number | Populated in 33% of sampled records. |
legal_name | string | Populated in 24% of sampled records. |
licence_number | string | Populated in 89% of sampled records. |
licence_type | string | Populated in 85% of sampled records. |
lng | number | Populated in 33% of sampled records. |
months_permit_to_open | number | Populated in 11% of sampled records. |
municipality | string | City slug. Call /{entity_type}/coverage for every slug in this dataset. Always present. |
neighbourhood | string | Populated in 47% of sampled records. |
normalized_address | string | Address normalized for joining the same property across datasets. Populated in 87% of sampled records. |
normalized_business_name | string | Business name normalized for joining the same business across datasets. Populated in 96% of sampled records. |
opened_on | string | Populated in 63% of sampled records. |
opened_on_precision | string | Populated in 63% of sampled records. |
permit_lookback_date | string | Populated in 11% of sampled records. |
permit_lookback_type | string | Populated in 11% of sampled records. |
phone | string | Populated in 30% of sampled records. |
postal_code | string | Populated in 36% of sampled records. |
preceded_by_permit | boolean | True when a building permit at this address preceded the licence. Populated in 59% of sampled records. |
province | string | Populated in 96% of sampled records. |
record_id | string | Stable identifier for this record. Pass it to the /{entity_type}/{record_id} endpoint. Always present. |
status | string | Always present. |
12 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 |
|---|---|---|
area_m2 | string | 3.7% |
cancel_date | string | 0.0% |
description | string | 3.7% |
is_opening | boolean | 7.1% |
licence_year | string | 3.7% |
naics_code | string | 3.7% |
num_employees | string | 3.7% |
number_of_employees | number | 3.7% |
operation_start_date | string | 3.7% |
permit_lookback_value | string | 5.8% |
ward | string | 3.5% |
zone | string | 3.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 →