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/contractor/{name_normalized}GET/permitGET/inspectionGET/licenceGET/development_permitGET/planning_applicationGET/assessmentGET/major_projectGET/heritage_statusGET/occupancy_permitGET/permit/statsGET/inspection/statsGET/licence/statsGET/development_permit/statsGET/planning_application/statsGET/assessment/statsGET/major_project/statsGET/heritage_status/statsGET/occupancy_permit/statsGET/permit/coverageGET/inspection/coverageGET/licence/coverageGET/development_permit/coverageGET/planning_application/coverageGET/assessment/coverageGET/major_project/coverageGET/heritage_status/coverageGET/occupancy_permit/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}GET/heritage_status/{record_id}GET/occupancy_permit/{record_id}Errors and troubleshooting
What each status code means, and how to read the message the API sends back.
Every BuildData error returns the same shape, so you can handle them all the same way:
{
"detail": "limit must be at most 500 (got 1000). Use offset, or cursor for deep pagination, to retrieve more."
}The detail string names the parameter at fault and, where there is one, the nearest valid value. Log it: it is written to be read by a person.
Status codes
| 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. |
Common mistakes
- An unknown city slug returns 422 naming the nearest real slug. Call
/{entity_type}/coveragefor every slug in a dataset. - A misspelled parameter returns 422 with a suggestion. Parameter names that are not close to a real one are ignored, so a filter you invented will silently do nothing.
- An empty
resultsarray is not an error. It means the filters matched nothing. Widen the date range or drop a filter to tell the difference between a bad query and a quiet week. - 504 means the query was too broad, not that the API is down. Add a
municipalityor a date range and retry. - On the free tier, the newest 30 days are not there. Responses carry a
free_tierobject saying so, and a record inside the window returns 403 rather than 404 when you ask for it by id. Everything older is included in full. - A point searches an area around itself.
latandlngwith noradius_kmsearch 1 km around the point, and the response echoes theradius_kmthat was applied. Send one of your own to widen or narrow it:?lat=43.6532&lng=-79.3832&radius_km=5. One coordinate without the other is still a 422. For every record at one address rather than everything within a kilometre of it, call /property with the address and skip the geocoding step.
Rate limits
Every response to a keyed request carries your quota, so you never have to guess how close you are:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per UTC day on your plan. |
X-RateLimit-Remaining | Requests left today. |
X-RateLimit-Reset | Seconds until the quota resets, which is midnight UTC. |
Past the limit the API answers 429 and adds Retry-After, the same number of seconds, so a client that backs off on that header waits exactly as long as it needs to rather than retrying into a wall. Nothing is charged for a refused request and the counter still rolls at midnight UTC.
Reading X-RateLimit-Remaining is cheaper than handling the 429: a job that pages through results can stop itself and resume tomorrow instead of failing halfway.