!

Scoring the environment around every Canadian property

Photo of Alex Wilkinson
Alex Wilkinson
CEO of Houski
2026-06-16

The environment a home sits in is one of the first things a buyer notices on a viewing and one of the last things any database captures. Is the air clean? Is the tap water good? Does the place smell like a rendering plant when the wind turns? Is it quiet at night? Is there green space within reach? Relocation buyers ask these questions out loud. Insurers and proptech platforms need the answers as data.

Houski carries five environment scores on every property, score_air_quality, score_water_quality, score_smell, score_quiet, and score_nature. They turn the soft, on-the-ground feel of a place into numbers you can filter, aggregate, and rank.

This post walks through what each score measures, how to pull all five for an address, how to compare neighbourhoods, and how relocation platforms and insurers can put the environment layer to work.

What the environment scores measure

Each score is an integer from 0 to 10 on every property, describing the area around it. Higher is always better.

  • score_air_quality. Local air quality. High scores mean clean air, low scores flag pollution sources nearby.
  • score_water_quality. Quality of nearby lakes, rivers, and other surface water. A high score means clean local waterways. It does not measure tap or drinking water.
  • score_smell. Freedom from unpleasant odours. A high score means the area smells fine. A low score flags industry, agriculture, or other odour sources nearby.
  • score_quiet. How quiet the area is. A high score means peace and quiet. A low score means noise from traffic, rail, flight paths, or nightlife.
  • score_nature. Access to green space and nature. A high score means parks, trees, and natural areas are close.

Because higher is always better, the filters read naturally. score_quiet_gte=8 returns the most peaceful end of the distribution. score_smell_lte=3 returns the areas most likely to have an odour problem.

A useful pairing to keep in mind. score_quiet and score_nature often move together in the suburbs and the countryside and diverge sharply in a dense, lively core, where nature can be high because of a big urban park while quiet is low because of the activity around it. The scores are deliberately separate so you can tell those two situations apart.

Pulling the environment layer for one address

Resolve a street address to a property_id first. This is a live call against the real API, regenerated on every render:

API request
TypeScript code
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', '1055+Canada+Pl+Vancouver+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);
})();
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.019999999552965164,
  "data": [
    {
      "address": "1055 Canada Place",
      "property_id": "61f7c61a0766a092"
    }
  ],
  "error": "",
  "match_meta": [
    {
      "match_value": 1.0,
      "property_id": "61f7c61a0766a092"
    }
  ],
  "price_quote": false,
  "result_total": 1,
  "time_ms": 1789
}

Then pull all five environment scores for that property_id at once:

API request
TypeScript code
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', '61f7c61a0766a092');
    url.searchParams.set('select', 'address,city,score_air_quality,score_water_quality,score_smell,score_quiet,score_nature');

    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);
})();
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.5299999713897705,
  "data": [
    {
      "address": "1055 Canada Place",
      "city": "Vancouver",
      "property_id": "61f7c61a0766a092",
      "score_air_quality": 6,
      "score_nature": 8,
      "score_quiet": 3,
      "score_smell": 6,
      "score_water_quality": 9
    }
  ],
  "error": "",
  "pagination": {
    "current_page": 1,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 1
  },
  "price_quote": false,
  "result_total": 1,
  "time_ms": 25,
  "ui_info": {
    "address": "1055 Canada Place",
    "address_link": "ca/bc/vancouver/downtown/1055-canada-place",
    "address_slug": "1055-canada-place",
    "city": "Vancouver",
    "city_id": "3d4b9ab2229768be",
    "city_link": "ca/bc/vancouver",
    "city_slug": "vancouver",
    "community": "Downtown",
    "community_id": "e7573c4d7d9f0078",
    "community_link": "ca/bc/vancouver/downtown",
    "community_slug": "downtown",
    "country": "Canada",
    "country_abbreviation": "CA",
    "country_abbreviation_id": "9ace2b6431b7f1be",
    "country_abbreviation_link": "ca",
    "country_slug": "canada",
    "property_id": "61f7c61a0766a092",
    "province": "British Columbia",
    "province_abbreviation": "BC",
    "province_abbreviation_id": "84b27e64bc119a2",
    "province_abbreviation_link": "ca/bc",
    "province_slug": "british_columbia"
  }
}

This downtown waterfront address shows the pattern cleanly. Water quality is excellent, air and smell are solid, nature access is high thanks to the harbour and nearby parks, and the one number that gives ground is quiet, because a busy downtown is never silent. That is the environment trade-off of a central location captured in five numbers instead of a vague impression.

Set select to only the scores you use.

Aggregating the environment across a city

The aggregate endpoint returns one number per market.

Median air quality for Hamilton, a city with a real industrial history:

API request
TypeScript code
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', 'Hamilton');
    url.searchParams.set('country_abbreviation', 'ca');
    url.searchParams.set('field', 'score_air_quality');
    url.searchParams.set('province_abbreviation', 'on');

    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);
})();
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 1.0,
  "data": [
    {
      "aggregation": "median",
      "field": "score_air_quality",
      "value": "5"
    }
  ],
  "error": "",
  "price_quote": false,
  "time_ms": 53
}

Mean nature access across all of Canada, as a national baseline:

API request
TypeScript code
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_nature');

    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);
})();
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 1.0,
  "data": [
    {
      "aggregation": "mean",
      "field": "score_nature",
      "value": "6.859"
    }
  ],
  "error": "",
  "price_quote": false,
  "time_ms": 25
}

The national mean is the line every neighbourhood sits above or below. A property well above it on nature access carries an amenity premium a relocation buyer will pay for, and one well below the line on air quality or smell is a flag worth surfacing before a client commits.

An environment comparison across markets

Run each score as a median across the markets you care about and you get an environment fingerprint per city. Each cell is the indicative median score from 0 to 10 with a plain read in brackets so the meaning is obvious: (good), (ok), (bad). Higher is always better, and numbers shift as the model updates:

CityProvinceAir qualityWater qualitySmellQuietNature
VancouverBC5 (ok)8 (good)5 (ok)3 (bad)8 (good)
VictoriaBC8 (good)8 (good)8 (good)5 (ok)8 (good)
HamiltonON5 (ok)5 (ok)5 (ok)5 (ok)5 (ok)
HalifaxNS8 (good)8 (good)8 (good)5 (ok)8 (good)
TorontoON5 (ok)5 (ok)5 (ok)3 (bad)5 (ok)

The lesson is the same as with every cluster of scores. No city is uniformly good or bad on the environment. The dense cores trade quiet away for everything else. The mid-sized coastal cities tend to score well across the board. A relocation client who says they want clean air and quiet is describing two different scores that do not always come together, and now you can show them exactly where they do.

How relocation platforms use this

A relocation or home-search platform can turn the five scores into a preference filter that actually reflects how people choose. Let a user weight what matters to them, quiet for a remote worker, nature for a family, air quality for someone with a respiratory condition, then rank inventory on the weighted combination. The property call to pull a city of inventory with the environment layer looks like this:

API request
Python code
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests

params = {}
params['api_key'] = 'YOUR_API_KEY'
params['city'] = 'Victoria'
params['country_abbreviation'] = 'ca'
params['has_expand_listings_eq'] = 'true'
params['province_abbreviation'] = 'bc'
params['results_per_page'] = '5'
params['select'] = 'address,score_air_quality,score_water_quality,score_smell,score_quiet,score_nature'

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}')
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 2.5999999046325684,
  "data": [
    {
      "address": "1468 Stroud Road",
      "property_id": "10046d004c2b9521",
      "score_air_quality": 8,
      "score_nature": 7,
      "score_quiet": 4,
      "score_smell": 7,
      "score_water_quality": 9
    },
    {
      "address": "816 Walker Street",
      "property_id": "100751448c333410",
      "score_air_quality": 8,
      "score_nature": 8,
      "score_quiet": 4,
      "score_smell": 7,
      "score_water_quality": 7
    },
    {
      "address": "705 250 Douglas Street",
      "property_id": "10354937608b506c",
      "score_air_quality": 8,
      "score_nature": 8,
      "score_quiet": 3,
      "score_smell": 7,
      "score_water_quality": 9
    },
    {
      "address": "206 327 Maitland Street",
      "property_id": "1038b8d2712ac5a4",
      "score_air_quality": 8,
      "score_nature": 9,
      "score_quiet": 4,
      "score_smell": 7,
      "score_water_quality": 7
    },
    {
      "address": "302 1034 Johnson Street",
      "property_id": "103a9d89ee2568ba",
      "score_air_quality": 8,
      "score_nature": 6,
      "score_quiet": 3,
      "score_smell": 6,
      "score_water_quality": 9
    }
  ],
  "error": "",
  "pagination": {
    "current_page": 1,
    "has_next_page": true,
    "has_previous_page": false,
    "page_total": 1264
  },
  "price_quote": false,
  "result_total": 6319,
  "time_ms": 35,
  "ui_info": {
    "city": "Victoria",
    "city_id": "3becf7bc3e2c3495",
    "city_link": "ca/bc/victoria",
    "city_slug": "victoria",
    "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"
  }
}

The has_expand_listings_eq=true filter narrows the pull to properties with active listings, the marketable inventory a home-search user actually wants, rather than the full universe of parcels. Page through to build the set, apply the user weights, and sort.

How insurers use this

For an insurer, the environment scores are early signals on hazards that show up later as claims. Persistent low air quality and low smell scores can correlate with proximity to industrial activity that carries its own exposure. Low quiet scores near major transport corridors can correlate with vibration and wear. None of these is a peril on its own, but as features alongside the catastrophe scores they sharpen a risk picture that beds and baths cannot.

Limitations to be honest about

  • Each score describes the area, not the exact lot. A well-treed property on a loud arterial road still inherits the area quiet score.
  • They update on a schedule, not in real time. A factory that closed last quarter may still weigh on the local smell score until the next refresh.
  • Each score is a relative ranking, not an absolute measurement. A quiet 8 is more peaceful than a 5. It is not a decibel reading.
  • The scores reflect the present and are built from public, government, and crowdsourced data, so very local conditions a neighbour would know about may not be captured.

A full example: shortlisting a relocation by environment

The workflow relocation advisers ask about most is the environment shortlist. A client is moving to a new city sight unseen and has told you, in plain words, that they want clean air, quiet, and green space.

Pull the inventory for the target city with the environment scores, then rank it on the client's priorities. The city pull is the call above. To check a specific candidate address the client found themselves, resolve it and pull its scores:

API request
Python code
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'] = '1055+Canada+Pl+Vancouver+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}')
API response
JSON
{
  "cache_hit": true,
  "cost_cents": 0.019999999552965164,
  "data": [
    {
      "address": "1055 Canada Place",
      "property_id": "61f7c61a0766a092"
    }
  ],
  "error": "",
  "match_meta": [
    {
      "match_value": 1.0,
      "property_id": "61f7c61a0766a092"
    }
  ],
  "price_quote": false,
  "result_total": 1,
  "time_ms": 1789
}

And the score pull against the returned property_id:

API request
Python code
from dataclasses import dataclass
from dataclasses_json import dataclass_json
import requests

params = {}
params['api_key'] = 'YOUR_API_KEY'
params['property_id_eq'] = '61f7c61a0766a092'
params['select'] = 'score_air_quality,score_water_quality,score_smell,score_quiet,score_nature'

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}')
API response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.5199999809265137,
  "data": [
    {
      "address": "1055 Canada Place",
      "property_id": "61f7c61a0766a092",
      "score_air_quality": 6,
      "score_nature": 8,
      "score_quiet": 3,
      "score_smell": 6,
      "score_water_quality": 9
    }
  ],
  "error": "",
  "pagination": {
    "current_page": 1,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 1
  },
  "price_quote": false,
  "result_total": 1,
  "time_ms": 46,
  "ui_info": {
    "address": "1055 Canada Place",
    "address_link": "ca/bc/vancouver/downtown/1055-canada-place",
    "address_slug": "1055-canada-place",
    "city": "Vancouver",
    "city_id": "3d4b9ab2229768be",
    "city_link": "ca/bc/vancouver",
    "city_slug": "vancouver",
    "community": "Downtown",
    "community_id": "e7573c4d7d9f0078",
    "community_link": "ca/bc/vancouver/downtown",
    "community_slug": "downtown",
    "country": "Canada",
    "country_abbreviation": "CA",
    "country_abbreviation_id": "9ace2b6431b7f1be",
    "country_abbreviation_link": "ca",
    "country_slug": "canada",
    "property_id": "61f7c61a0766a092",
    "province": "British Columbia",
    "province_abbreviation": "BC",
    "province_abbreviation_id": "84b27e64bc119a2",
    "province_abbreviation_link": "ca/bc",
    "province_slug": "british_columbia"
  }
}

In your own code, weight the three scores the client cares about, sort the inventory descending, and hand them a shortlist where the top of the list is the best environmental fit rather than the cheapest or the biggest. The numbers behind the ranking are the same ones you can show them to explain why.

What to build next

Whether you are building a home-search filter, ranking a relocation shortlist, or adding environment features to a risk model, it starts with one property call or one aggregate call. The fields are documented, the filters compose cleanly, and a city-wide aggregate costs pennies. A full per-property pull of a city costs real money, so start with aggregates and pull property-level rows only where you need them.

Get started at /api-documentation/quick-start. Pricing starts at $99 a month, and you can have an environment comparison running in your terminal in under five minutes.