Canada has run through several of its worst wildfire seasons on record in the last few years. Fire has reached communities from the British Columbia Interior to the Alberta Rockies, across the northern Prairies, into the boreal stretches of Ontario and Quebec, and up to the edge of the Northwest Territories. Jasper lost a large share of its townsite in 2024. Yellowknife emptied out under evacuation order in 2023. Lytton, years after the fire that wiped out the village, is still rebuilding. By the time you read this in May 2026, the first open-burn bans of the year are already in place across multiple provinces.
The United States has had the same run. Paradise burned in 2018, Lahaina in 2023, the grassland suburbs of Superior and Louisville outside Boulder in the last days of 2021, and in January 2025 the Palisades and Eaton fires took thousands of homes inside the Los Angeles city limits and became the costliest wildfire event on record. Insurers have stopped writing new business in parts of California, and the state's insurer of last resort has become the biggest writer in the interface.
If you own property in either country, work in real estate, write insurance, or report on housing, "how exposed is this address to wildfire" is no longer an abstract question. It shows up in mortgage conditions, in renewal letters, in offers that get pulled the week before close.
The problem is that almost everyone is still answering it at the wrong resolution. Insurance underwriting often falls back to a postal code, a forward sortation area, or a ZIP code, and any of those can cover half a city. A buyer comparing a neighbourhood tucked against a forested ridgeline with one a few kilometres away in the urban core gets the same answer for both.
That gap is what Houski's score_fire field is built to close. It is a neighbourhood-scale score, resolved to roughly a 1.5 kilometre window around each property, which is far finer than the postal-code, forward-sortation-area, and ZIP code geographies most underwriting falls back to. This post walks through what the score is, how to pull it from the Houski API at the address level and aggregated across communities in both countries, and how to combine it with construction details to find properties that are genuinely hardened against fire instead of just lucky on the map.
What score_fire actually measures
score_fire is a 0 to 10 integer on properties in both countries, and on properties in the other countries Houski covers too, ranked within each country. Higher is better. A score of 9 in downtown Toronto or downtown Chicago means the property has very low modelled wildfire vulnerability. A score of 2 on a slope above a boreal treeline, or on a brush-lined canyon edge above Los Angeles, means the property has high modelled vulnerability and an underwriter should look twice.
The model behind it is an area-level exposure score. It is computed across a roughly 1.5 kilometre neighbourhood window around each property, from wildfire history and the fuel in the surrounding landscape. That window is deliberate. What burns houses is the fuel around them, not the cleared lot they sit on.
It does not look at building materials. That is intentional. score_fire describes the situation the building is sitting in. Combine it with exterior_finish, roof_material, and construction_year (covered later in this post) to evaluate the building itself.
Because higher is better, the filter operators read naturally. score_fire_gte=8 returns the safer end of the distribution. score_fire_lte=4 returns the properties that need the most attention.
Pulling score_fire for a single address
Start with a real Canadian address. We will look up a property in West Kelowna, an area that has spent several recent summers on evacuation alert. The same workflow runs identically for an address in Jasper, Halifax, Kapuskasing, or anywhere else in either country.
Two-step lookup: resolve the address to a property_id with /search, then pull the fields you want from /properties. The block below is a live call against the real API. The request code and the JSON response are generated every time this page is rendered, so they always reflect the current schema and current data:
const houski_data = async (): Promise<SearchResponse> => {
// You must copy the SearchResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/search');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('max_results', '1');
url.searchParams.set('query', '2575+Boucherie+Rd+West+Kelowna+BC');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: SearchResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 0.02,
"data": [
{
"address": "2775 Boucherie Road",
"property_id": "38bea0bd31929180"
}
],
"error": "",
"match_meta": [
{
"match_value": 0.9166667,
"property_id": "38bea0bd31929180"
}
],
"price_quote": false,
"result_total": 1,
"time_ms": 35
}
Once you have the property_id, pull the fire score and the structural fields you need to interpret it. The id in the block below is a sample property, so swap in the property_id your own search returned:
const houski_data = async (): Promise<PropertiesResponse> => {
// You must copy the PropertiesResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/properties');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('property_id_eq', 'bd9c6fb24c31c772');
url.searchParams.set('select', 'address,city,score_fire,exterior_finish,roof_material,construction_year,interior_sq_m,land_area_sq_m');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: PropertiesResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 0.63,
"data": [
{
"address": "302 610 17 Avenue SW",
"city": "Calgary",
"construction_year": 1979,
"exterior_finish": "Brick",
"interior_sq_m": 92.438484,
"land_area_sq_m": 0.0,
"property_id": "bd9c6fb24c31c772",
"roof_material": "Asphalt",
"score_fire": 10
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": false,
"has_previous_page": false,
"page_total": 1
},
"price_quote": false,
"result_total": 1,
"time_ms": 14,
"ui_info": {
"address": "302 610 17 Avenue SW",
"address_link": "ca/ab/calgary/beltline/302-610-17-avenue-sw",
"address_slug": "302-610-17-avenue-sw",
"city": "Calgary",
"city_id": "6ec95b53075d062c",
"city_link": "ca/ab/calgary",
"city_slug": "calgary",
"community": "Beltline",
"community_id": "ecc51da246c7dd4a",
"community_link": "ca/ab/calgary/beltline",
"community_slug": "beltline",
"country": "Canada",
"country_abbreviation": "CA",
"country_abbreviation_id": "9ace2b6431b7f1be",
"country_abbreviation_link": "ca",
"country_slug": "canada",
"parent_address": "610 17 Avenue SW",
"parent_property_id": "52a7d622eafe5319",
"property_id": "bd9c6fb24c31c772",
"province": "Alberta",
"province_abbreviation": "AB",
"province_abbreviation_id": "aae1f05a0f89d2c7",
"province_abbreviation_link": "ca/ab",
"province_slug": "alberta"
}
}
Then the same two steps for an American address, a house in the foothills above Sunland-Tujunga inside the Los Angeles city limits:
const houski_data = async (): Promise<SearchResponse> => {
// You must copy the SearchResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/search');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('max_results', '1');
url.searchParams.set('query', '8827+Mulberry+Dr+Los+Angeles+CA');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: SearchResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 0.02,
"data": [
{
"address": "8827 Mulberry Drive",
"property_id": "10024a1aa6254922"
}
],
"error": "",
"match_meta": [
{
"match_value": 1.0,
"property_id": "10024a1aa6254922"
}
],
"price_quote": false,
"result_total": 1,
"time_ms": 38
}
const houski_data = async (): Promise<PropertiesResponse> => {
// You must copy the PropertiesResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/properties');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('property_id_eq', '10024a1aa6254922');
url.searchParams.set('select', 'address,city,score_fire,exterior_finish,roof_material,construction_year,interior_sq_m,land_area_sq_m');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: PropertiesResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 0.63,
"data": [
{
"address": "8827 Mulberry Drive",
"city": "Los Angeles",
"construction_year": 1964,
"exterior_finish": "Stucco",
"interior_sq_m": 131.27199,
"land_area_sq_m": 837.9854,
"property_id": "10024a1aa6254922",
"roof_material": "Asphalt",
"score_fire": 2
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": false,
"has_previous_page": false,
"page_total": 1
},
"price_quote": false,
"result_total": 1,
"time_ms": 12,
"ui_info": {
"address": "8827 Mulberry Drive",
"address_link": "us/ca/los-angeles/unknown/8827-mulberry-drive",
"address_slug": "8827-mulberry-drive",
"city": "Los Angeles",
"city_id": "5ec4901ef1cad82d",
"city_link": "us/ca/los-angeles",
"city_slug": "los_angeles",
"community": "Unknown",
"community_link": "us/ca/los-angeles/unknown",
"country": "United States",
"country_abbreviation": "US",
"country_abbreviation_id": "c148472ce4bd39d",
"country_abbreviation_link": "us",
"country_slug": "united_states",
"property_id": "10024a1aa6254922",
"province": "California",
"province_abbreviation": "CA",
"province_abbreviation_id": "cee8ffd99afb31c6",
"province_abbreviation_link": "us/ca",
"province_slug": "california"
}
}
The select parameter is mandatory in spirit even when the docs do not strictly require it. Every field you do not request is a field you do not pay for.
A low score_fire result combined with vinyl siding and asphalt shingles is the profile that insurers are starting to surcharge or decline outright. The same address with a metal roof and Hardie board would still have the same score_fire, but the combined risk story changes.
Aggregating score_fire across communities
The /aggregate endpoint is where this gets useful for planners, journalists, and anyone trying to compare neighbourhoods rather than individual lots. It takes the same filter parameters as /properties but returns a single aggregated value.
Median fire safety score for the city of Kelowna:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'median');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('city', 'Kelowna');
url.searchParams.set('country_abbreviation', 'ca');
url.searchParams.set('field', 'score_fire');
url.searchParams.set('province_abbreviation', 'bc');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "median",
"field": "score_fire",
"value": "6"
}
],
"error": "",
"price_quote": false,
"time_ms": 4
}
Mean across all of Canada:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'mean');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('country_abbreviation', 'ca');
url.searchParams.set('field', 'score_fire');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "mean",
"field": "score_fire",
"value": "5.882"
}
],
"error": "",
"price_quote": false,
"time_ms": 21
}
Mean across the United States:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'mean');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('country_abbreviation', 'us');
url.searchParams.set('field', 'score_fire');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "mean",
"field": "score_fire",
"value": "8.242"
}
],
"error": "",
"price_quote": false,
"time_ms": 228
}
Count of high-vulnerability properties (score 3 and below) in West Kelowna:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'count');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('city', 'West+Kelowna');
url.searchParams.set('country_abbreviation', 'ca');
url.searchParams.set('field', 'score_fire');
url.searchParams.set('province_abbreviation', 'bc');
url.searchParams.set('score_fire_lte', '3');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "count",
"field": "score_fire",
"value": "8994"
}
],
"error": "",
"price_quote": false,
"time_ms": 7
}
And the same count inside the Los Angeles city limits:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'count');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('city', 'Los+Angeles');
url.searchParams.set('country_abbreviation', 'us');
url.searchParams.set('field', 'score_fire');
url.searchParams.set('province_abbreviation', 'ca');
url.searchParams.set('score_fire_lte', '3');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "count",
"field": "score_fire",
"value": "74861"
}
],
"error": "",
"price_quote": false,
"time_ms": 20
}
One request, one number. No per-property charges to scan a city.
Building a neighbourhood comparison
For a "which city is safest" table, we would loop the same /aggregate call across every city of interest (Toronto, Montreal, Halifax, Vancouver, Victoria, Calgary, Edmonton, Kelowna, West Kelowna, Kamloops, Prince George, Fort McMurray, Yellowknife, and on the American side Los Angeles, San Diego, Seattle, Denver, Phoenix, Scottsdale, Boulder, Reno, Bend, Redding, Malibu, Paradise), then sort by median score and join in a high-vulnerability count per city. Swap country_abbreviation, province_abbreviation and city for each row. The underlying per-city call is:
const houski_data = async (): Promise<AggregateResponse> => {
// You must copy the AggregateResponse type declarations from the
// Houski API documentation to strongly type the response
const url = new URL('https://api.houski.ca/aggregate');
url.searchParams.set('aggregation', 'median');
url.searchParams.set('api_key', 'YOUR_API_KEY');
url.searchParams.set('city', 'Kelowna');
url.searchParams.set('country_abbreviation', 'ca');
url.searchParams.set('field', 'score_fire');
url.searchParams.set('province_abbreviation', 'bc');
const response = await fetch(url);
const data = await response.json();
return data;
}
(async () => {
let data: AggregateResponse = await houski_data();
// Log the response
console.log(data);
})();
{
"cache_hit": true,
"cost_cents": 1.0,
"data": [
{
"aggregation": "median",
"field": "score_fire",
"value": "6"
}
],
"error": "",
"price_quote": false,
"time_ms": 4
}
Run this once a quarter and you have a defensible time series for any client conversation about how a market is shifting.
Reading the comparison
The table that loop produces has one row per city, the median fire safety score, and the count of properties scoring 3 or less. Run it for current values, since the numbers shift as the score refreshes and as new construction is added.
Two patterns are worth looking for. First, dense urban cores tend to sit well above interface towns, which lines up with both fuel type and ignition history. Second, neighbouring cities can differ sharply even when they share a lake and a postal-code prefix, and that gap is the exact kind of signal that a forward sortation area model erases.
Read the count column beside the median. A city median hides the interface: a big city can score high at the median and still hold a large number of high-vulnerability homes, because its flats are enormous and its hillside neighbourhoods are not. And grassland burns as well as forest, so a suburb on open grass can score as badly as a forested valley. Fuel is fuel.
Combining score_fire with construction details
Two properties with the same score_fire can have very different total exposure once you factor in the building. A 1972 wood-clad house with cedar shake roof on a 6 score_fire lot is in worse shape than a 2019 Hardie-clad house with a standing-seam metal roof on the same lot.
The Houski API exposes the structural fields you need to make that distinction. exterior_finish values include Vinyl, Wood, Stucco, Brick, Stone, Concrete, Concrete block, Metal, Aluminum, Steel, Plaster, and Log. roof_material covers Asphalt, Clay, Slate, Metal, Wood, Concrete, Solar tile and Plastic. construction_year is an integer.
A useful pattern: pull score_fire for the property together with the structural fields, then layer a construction multiplier on top in your own code. The underlying API call to pull a sample of detached homes in one city looks like this:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['city'] = 'west+kelowna'
params['country_abbreviation'] = 'ca'
params['property_type_eq'] = 'House'
params['province_abbreviation'] = 'bc'
params['results_per_page'] = '5'
params['select'] = 'property_id,address,score_fire,exterior_finish,roof_material,construction_year'
response = requests.get('https://api.houski.ca/properties', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the PropertiesResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = PropertiesResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 2.1,
"data": [
{
"address": "2750 Casa Loma Road",
"construction_year": 1965,
"exterior_finish": "Brick",
"property_id": "1041b542821e5cac",
"roof_material": "Asphalt",
"score_fire": 8
},
{
"address": "2741 Benedick Road",
"construction_year": 1972,
"exterior_finish": "Stucco",
"property_id": "137d6581e1f64f0d",
"roof_material": "Asphalt",
"score_fire": 8
},
{
"address": "2594 Lucinde Road",
"construction_year": 2006,
"exterior_finish": "Stucco",
"property_id": "148ab1c2948022f0",
"roof_material": "Asphalt",
"score_fire": 8
},
{
"address": "2300 Campbell Road",
"construction_year": 1964,
"exterior_finish": "Stucco",
"property_id": "1690b7de36a4662c",
"roof_material": "Asphalt",
"score_fire": 8
},
{
"address": "2554 Lucinde Road",
"construction_year": 2007,
"exterior_finish": "Stucco",
"property_id": "17c9642e298af86",
"roof_material": "Asphalt",
"score_fire": 8
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": true,
"has_previous_page": false,
"page_total": 2132
},
"price_quote": false,
"result_total": 10658,
"time_ms": 10,
"ui_info": {
"city": "West Kelowna",
"city_id": "d76a97e37967d5b1",
"city_link": "ca/bc/west-kelowna",
"city_slug": "west_kelowna",
"country": "Canada",
"country_abbreviation": "CA",
"country_abbreviation_id": "9ace2b6431b7f1be",
"country_abbreviation_link": "ca",
"country_slug": "canada",
"province": "British Columbia",
"province_abbreviation": "BC",
"province_abbreviation_id": "84b27e64bc119a2",
"province_abbreviation_link": "ca/bc",
"province_slug": "british_columbia"
}
}
And the same pull for the low-scoring end of Los Angeles, where the stucco-and-asphalt hillside stock of the 1950s and 1960s is the profile that burned in January 2025:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['city'] = 'los+angeles'
params['country_abbreviation'] = 'us'
params['property_type_eq'] = 'House'
params['province_abbreviation'] = 'ca'
params['results_per_page'] = '5'
params['score_fire_lte'] = '3'
params['select'] = 'property_id,address,score_fire,exterior_finish,roof_material,construction_year'
response = requests.get('https://api.houski.ca/properties', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the PropertiesResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = PropertiesResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 2.1,
"data": [
{
"address": "8827 Mulberry Drive",
"construction_year": 1964,
"exterior_finish": "Stucco",
"property_id": "10024a1aa6254922",
"roof_material": "Asphalt",
"score_fire": 2
},
{
"address": "8333 Apperson Street",
"construction_year": 1962,
"exterior_finish": "Stucco",
"property_id": "1003cb910cc3232f",
"roof_material": "Asphalt",
"score_fire": 2
},
{
"address": "24236 Archwood Street",
"construction_year": 1960,
"exterior_finish": "Stucco",
"property_id": "1003eecbd1233c0d",
"roof_material": "Asphalt",
"score_fire": 2
},
{
"address": "11027 Oro Vista Avenue",
"construction_year": 1954,
"exterior_finish": "Stucco",
"property_id": "100426e939b457c5",
"roof_material": "Asphalt",
"score_fire": 2
},
{
"address": "2342 Westridge Road",
"construction_year": 1964,
"exterior_finish": "Stucco",
"property_id": "100520ca291842d1",
"roof_material": "Asphalt",
"score_fire": 2
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": true,
"has_previous_page": false,
"page_total": 11942
},
"price_quote": false,
"result_total": 59709,
"time_ms": 18,
"ui_info": {
"city": "Los Angeles",
"city_id": "5ec4901ef1cad82d",
"city_link": "us/ca/los-angeles",
"city_slug": "los_angeles",
"country": "United States",
"country_abbreviation": "US",
"country_abbreviation_id": "c148472ce4bd39d",
"country_abbreviation_link": "us",
"country_slug": "united_states",
"province": "California",
"province_abbreviation": "CA",
"province_abbreviation_id": "cee8ffd99afb31c6",
"province_abbreviation_link": "us/ca",
"province_slug": "california"
}
}
In your own code, apply a building-hardness multiplier (Brick and Concrete near 1.0, Vinyl around 0.7, Wood and Log near 0.55), a roof multiplier (Metal and Slate near 1.0, Asphalt around 0.75, Wood roofs near 0.4), and a construction-era factor that rewards post-2010 builds. Multiply score_fire by the three factors to get a combined safety score, then plot raw score_fire on the x-axis and combined safety on the y-axis with a dashed y=x reference line for "construction does not change anything".
Most of the dots fall well below the line because most construction choices on a high-vulnerability lot do not fully compensate. The dots that hug the line are the rare combinations of stucco or brick exterior, metal or clay roof, and post-2010 build year.
For an underwriter, that scatter plot is the difference between "decline this address" and "tier this address into preferred at a small surcharge".
How insurers can use this for tiered underwriting
Personal property carriers writing across Canada are under pressure on two sides. Reinsurance treaties keep tightening on wildfire exposure in the Interior, the Rockies, and the boreal belt. Brokers are being asked by clients to find them coverage at any price after a non-renewal letter. In the United States the pressure is sharper still. Carriers have stopped writing new homeowners policies across parts of California, the state's insurer of last resort has swelled, and the regulator now expects catastrophe models and mitigation credits in rate filings, which is exactly a per-address score plus a construction multiplier. The middle ground in both countries is risk-based pricing that does not just blanket the entire forward sortation area or ZIP code.
A workable tier structure:
- Preferred. score_fire 7.5 and above, exterior in (Brick, Concrete, Stucco, Metal), roof in (Metal, Slate, Clay, Concrete), construction_year 1990 and later. Standard rates, full limits.
- Standard. score_fire 5 to 7.4 with any reasonable construction. Standard rates with an inspection clause.
- Surcharged. score_fire 3 to 4.9 or older wood-frame on better lots. Surcharge plus a defensible-space attestation.
- Restricted. score_fire below 3, especially when paired with wood roof or pre-1990 wood exterior. Bind only with a FireSmart Canada or Firewise USA certificate, or the state's own defensible-space attestation, lower limits, higher deductible.
Pull the inputs at quote time with one /properties call. The cost per quote is a fraction of the loss adjustment expense of one disputed claim from a fire that should have been priced into the policy.
Limitations to be honest about
The model has limits. Be straight with clients about them.
- It updates on a schedule, not in real time. A fire perimeter from last August is in. A fire that started last week is not.
- It does not see building code retrofits. If an owner replaced cedar shakes with standing-seam metal and it has not reached the records yet, the field still says Wood.
- It does not reward enrolled FireSmart Canada or Firewise USA properties or municipal wildfire mitigation programs. Both materially change real risk. Both are invisible to a model built on public, government, and crowdsourced data sources.
- score_fire is a relative ranking, not an annual probability. A score of 8 is safer than a score of 4. It does not tell you that the score 4 property burns once every N years.
For most use cases (underwriting tiers, neighbourhood comparison, portfolio screening) those limits are acceptable as long as you state them. For evacuation planning at the structure-by-structure level, pair the field with on-the-ground inspection.
A full example: scoring a portfolio of 50 rentals
This is the workflow I get asked about most often by owners of small rental portfolios in fire-exposed regions. You have 30 to 80 doors spread across a few cities, maybe across provinces or states. Your insurer dropped one of them at renewal. You want to know which of the others are next.
The pattern is two API calls per property: a /search call to resolve a street address to a property_id, then a /properties call for the fields. We would loop through something like this for every row in the rent roll. The search step is:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['max_results'] = '1'
params['query'] = '2575+Boucherie+Rd+West+Kelowna+BC'
response = requests.get('https://api.houski.ca/search', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the SearchResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = SearchResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 0.02,
"data": [
{
"address": "2775 Boucherie Road",
"property_id": "38bea0bd31929180"
}
],
"error": "",
"match_meta": [
{
"match_value": 0.9166667,
"property_id": "38bea0bd31929180"
}
],
"price_quote": false,
"result_total": 1,
"time_ms": 35
}
And the structural lookup against the returned property_id is:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['property_id_eq'] = 'bd9c6fb24c31c772'
params['select'] = 'score_fire,exterior_finish,roof_material,construction_year'
response = requests.get('https://api.houski.ca/properties', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the PropertiesResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = PropertiesResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 0.42,
"data": [
{
"address": "302 610 17 Avenue SW",
"construction_year": 1979,
"exterior_finish": "Brick",
"property_id": "bd9c6fb24c31c772",
"roof_material": "Asphalt",
"score_fire": 10
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": false,
"has_previous_page": false,
"page_total": 1
},
"price_quote": false,
"result_total": 1,
"time_ms": 10,
"ui_info": {
"address": "302 610 17 Avenue SW",
"address_link": "ca/ab/calgary/beltline/302-610-17-avenue-sw",
"address_slug": "302-610-17-avenue-sw",
"city": "Calgary",
"city_id": "6ec95b53075d062c",
"city_link": "ca/ab/calgary",
"city_slug": "calgary",
"community": "Beltline",
"community_id": "ecc51da246c7dd4a",
"community_link": "ca/ab/calgary/beltline",
"community_slug": "beltline",
"country": "Canada",
"country_abbreviation": "CA",
"country_abbreviation_id": "9ace2b6431b7f1be",
"country_abbreviation_link": "ca",
"country_slug": "canada",
"parent_address": "610 17 Avenue SW",
"parent_property_id": "52a7d622eafe5319",
"property_id": "bd9c6fb24c31c772",
"province": "Alberta",
"province_abbreviation": "AB",
"province_abbreviation_id": "aae1f05a0f89d2c7",
"province_abbreviation_link": "ca/ab",
"province_slug": "alberta"
}
}
The same two calls for a Reno address on the Sierra fringe, where the Caughlin Ranch and Mogul neighbourhoods have watched fire come down the slope more than once:
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['max_results'] = '1'
params['query'] = '4425+Gibraltar+Dr+Reno+NV'
response = requests.get('https://api.houski.ca/search', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the SearchResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = SearchResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 0.02,
"data": [
{
"address": "4425 Gibraltar Drive",
"property_id": "101e1d7cbc0dd523"
}
],
"error": "",
"match_meta": [
{
"match_value": 1.0,
"property_id": "101e1d7cbc0dd523"
}
],
"price_quote": false,
"result_total": 1,
"time_ms": 30
}
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests
params = {}
params['api_key'] = 'YOUR_API_KEY'
params['property_id_eq'] = '101e1d7cbc0dd523'
params['select'] = 'score_fire,exterior_finish,roof_material,construction_year'
response = requests.get('https://api.houski.ca/properties', params=params)
if response.status_code == 200:
json_data = response.json()
print(json_data)
# You must copy the PropertiesResponse type declarations from the
# Houski API documentation to strongly type the response
typed_response = PropertiesResponse.from_dict(json_data)
# Log the response
print(typed_response)
else:
print(f'Failed to get data: {response}')
{
"cache_hit": true,
"cost_cents": 0.42,
"data": [
{
"address": "4425 Gibraltar Drive",
"construction_year": 1973,
"exterior_finish": "Brick",
"property_id": "101e1d7cbc0dd523",
"roof_material": "Asphalt",
"score_fire": 2
}
],
"error": "",
"pagination": {
"current_page": 1,
"has_next_page": false,
"has_previous_page": false,
"page_total": 1
},
"price_quote": false,
"result_total": 1,
"time_ms": 11,
"ui_info": {
"address": "4425 Gibraltar Drive",
"address_link": "us/nv/reno/unknown/4425-gibraltar-drive",
"address_slug": "4425-gibraltar-drive",
"city": "Reno",
"city_id": "e8f555fdf292f35",
"city_link": "us/nv/reno",
"city_slug": "reno",
"community": "Unknown",
"community_link": "us/nv/reno/unknown",
"country": "United States",
"country_abbreviation": "US",
"country_abbreviation_id": "c148472ce4bd39d",
"country_abbreviation_link": "us",
"country_slug": "united_states",
"property_id": "101e1d7cbc0dd523",
"province": "Nevada",
"province_abbreviation": "NV",
"province_abbreviation_id": "4d6afaf9cc1522f5",
"province_abbreviation_link": "us/nv",
"province_slug": "nevada"
}
}
Bucket the resulting score_fire values into risk tiers (restricted at 3 and below, surcharged at 3 to 4.9, standard at 5 to 7.4, preferred at 7.5 and up), then sort ascending and write the CSV. The first row is the property your insurer is most likely to drop next. Walk that one first. Schedule a FireSmart or Firewise assessment, photograph the defensible space, swap the roof if the budget is there. Move down the list.
Where the exposure concentrates
Most of the wildfire conversation focuses on a handful of headline regions, but the model scores properties across both countries. Regions worth a closer look:
- The British Columbia Interior and the Okanagan. The interface is dense and the ignition history is long. Compare the medians of West Kelowna, Kamloops, and Prince George against the coastal cities.
- The Alberta Rockies and the northern Prairies. Jasper in 2024 and Fort McMurray in 2016 both showed how fast an interface community can go. Worth a tier conversation for any policy written there.
- The boreal belt across northern Ontario, Quebec, and the territories. Yellowknife and the surrounding Northwest Territories communities carry real interface exposure, and the 2023 season made that plain.
- The Sea-to-Sky corridor and Vancouver Island fringes. Squamish, Sooke, Pemberton, and Whistler all pick up on slope, fuel, and proximity to wilderness even though they sit near the coast.
- The Sierra Nevada foothills and the Southern California ranges. Paradise, Malibu, and the hillside neighbourhoods of Los Angeles and San Diego sit on some of the most fire-prone landscape in the United States.
- The Front Range and the mountain West. Superior and Louisville outside Boulder, the western edge of Colorado Springs, Reno's Sierra fringe, Boise's foothills, and Bend are worth checking against their state medians.
- The Southwest interface. Scottsdale's desert edge, and Flagstaff and Prescott under ponderosa pine, pick up the same signals in a different fuel type.
- The Pacific Northwest east of the Cascades. Wenatchee and Spokane carry the same fuel and slope signals as the British Columbia Interior across the border.
For each of these, the same /aggregate call with a different country, province or state, and city value gives you the local median. Pair it with a quick scan of score_fire_lte=3 properties to find the long tail of high-vulnerability addresses.
What to build next
If you got this far, you probably want to do one of three things: pull the score for a portfolio you already have, score a city you are about to enter, or wire it into a quoting flow. All three start the same way, with one /properties call or one /aggregate call. The fields are documented, the filters compose cleanly, and the per-request cost is small enough that scanning a whole city in either country for a one-time analysis is a coffee, not a contract.
Get started at /api-documentation/quick-start. Pricing starts at $99 a month, and you can have a working cross-border comparison in your terminal in under five minutes.
