!

Filtering

Most Houski API endpoints let you filter by any field, or geographically via bounding box or polygon (examples below).

Filter by adding query parameters to the request URL. To filter the properties endpoint by full bathrooms, use any of:

FilterNameExample
Greater thangtbathroom_full_gt=3
Greater than or equal togtebathroom_full_gte=3
Less thanltbathroom_full_lt=3
Less than or equal toltebathroom_full_lte=3
Equal toeqbathroom_full_eq=3
Not equal toneqbathroom_full_neq=3
In the given listinproperty_type_in=House,Apartment
Matches a regular expressionregexbathroom_full_regex=1|3
Does not match a regular expressionnregexbathroom_full_nregex=1|3
Programming language

Select the programming language you want to display the code examples in.

Filtering by number of bedrooms
Request
Shell session
curl -X GET "https://api.houski.ca/properties?=3&api_key=YOUR_API_KEY&bedroom_eq=3&country_abbreviation=ca&province_abbreviation=ab&select=bedroom,den,estimate_list_price,latitude,longitude"
TypeScript code
const houski_properties_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('', '3');
    url.searchParams.set('api_key', 'YOUR_API_KEY');
    url.searchParams.set('bedroom_eq', '3');
    url.searchParams.set('country_abbreviation', 'ca');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('select', 'bedroom,den,estimate_list_price,latitude,longitude');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.0,
  "data": [],
  "error": "Invalid query parameters: ''. If you are trying to filter, you may be missing the operator suffix, as in 'bedroom_eq=2' not 'bedroom=2'.",
  "pagination": {
    "current_page": 0,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 0
  },
  "price_quote": false,
  "result_total": 0,
  "time_ms": 2,
  "ui_info": {}
}
Filtering by a bounding box
A bounding box is a rectangular geographic region defined by its northeast and southwest corner coordinates. Requests using these parameters only return properties inside the box.
Request
Shell session
curl -X GET "https://api.houski.ca/properties?api_key=YOUR_API_KEY&bbox_ne_lat=51.1005&bbox_ne_lng=-113.508&bbox_sw_lat=50.6668&bbox_sw_lng=-114.4593&country_abbreviation=ca&page=1&province_abbreviation=ab&results_per_page=3&select=bedroom,den,estimate_list_price,latitude,longitude"
TypeScript code
const houski_properties_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('bbox_ne_lat', '51.1005');
    url.searchParams.set('bbox_ne_lng', '-113.508');
    url.searchParams.set('bbox_sw_lat', '50.6668');
    url.searchParams.set('bbox_sw_lng', '-114.4593');
    url.searchParams.set('country_abbreviation', 'ca');
    url.searchParams.set('page', '1');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('results_per_page', '3');
    url.searchParams.set('select', 'bedroom,den,estimate_list_price,latitude,longitude');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 1.0199999809265137,
  "data": [
    {
      "address": "6 1744 7 Street SW",
      "bedroom": 3,
      "den": 0,
      "estimate_list_price": 660525,
      "latitude": 51.03611755371094,
      "longitude": -114.0792007446289,
      "property_id": "10004f7afe0c1946"
    },
    {
      "address": "384 Copperpond Landng SE",
      "bedroom": 3,
      "den": 0,
      "estimate_list_price": 389896,
      "latitude": 50.925743103027344,
      "longitude": -113.92975616455078,
      "property_id": "10007f9761f49940"
    },
    {
      "address": "52 Cedargrove Way SW",
      "bedroom": 3,
      "den": 0,
      "estimate_list_price": 635789,
      "latitude": 50.95145034790039,
      "longitude": -114.12571716308594,
      "property_id": "1000c277cd905d3b"
    }
  ],
  "error": "",
  "pagination": {
    "current_page": 1,
    "has_next_page": true,
    "has_previous_page": false,
    "page_total": 178814
  },
  "price_quote": false,
  "result_total": 536441,
  "time_ms": 91,
  "ui_info": {
    "country": "Canada",
    "country_abbreviation": "CA",
    "country_abbreviation_id": "9ace2b6431b7f1be",
    "country_abbreviation_link": "ca",
    "country_slug": "canada",
    "province": "Alberta",
    "province_abbreviation": "AB",
    "province_abbreviation_id": "aae1f05a0f89d2c7",
    "province_abbreviation_link": "ca/ab",
    "province_slug": "alberta"
  }
}
Filtering by a polygon
A polygon is an arbitrary shape defined by ordered latitude-longitude pairs. Only properties inside the polygon are returned. Format: lat-lng pairs separated by underscores, with each pair separated by a comma.
Request
Shell session
curl -X GET "https://api.houski.ca/properties?api_key=YOUR_API_KEY&country_abbreviation=ca&page=1&polygon=51.0447_-114.0719,51.0544_-114.0719,51.0544_-114.0856,51.0452_-114.0856&province_abbreviation=ab&results_per_page=3&select=bedroom,den,estimate_list_price,latitude,longitude"
TypeScript code
const houski_properties_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('country_abbreviation', 'ca');
    url.searchParams.set('page', '1');
    url.searchParams.set('polygon', '51.0447_-114.0719,51.0544_-114.0719,51.0544_-114.0856,51.0452_-114.0856');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('results_per_page', '3');
    url.searchParams.set('select', 'bedroom,den,estimate_list_price,latitude,longitude');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 1.0199999809265137,
  "data": [
    {
      "address": "2018 608 9 Street SW",
      "bedroom": 3,
      "den": 0,
      "estimate_list_price": 710248,
      "latitude": 51.04768371582031,
      "longitude": -114.0837173461914,
      "property_id": "100556b0922cfec"
    },
    {
      "address": "2101 888 4 Avenue SW",
      "bedroom": 1,
      "den": 0,
      "estimate_list_price": 1279105,
      "latitude": 51.05002975463867,
      "longitude": -114.08051300048828,
      "property_id": "100a3285ac5dd6b1"
    },
    {
      "address": "1001 730 2 Avenue SW",
      "bedroom": 2,
      "den": 0,
      "estimate_list_price": 415302,
      "latitude": 51.0519905090332,
      "longitude": -114.0774917602539,
      "property_id": "100f44b3b8d53d1c"
    }
  ],
  "error": "",
  "pagination": {
    "current_page": 1,
    "has_next_page": true,
    "has_previous_page": false,
    "page_total": 3082
  },
  "price_quote": false,
  "result_total": 9244,
  "time_ms": 144,
  "ui_info": {
    "country": "Canada",
    "country_abbreviation": "CA",
    "country_abbreviation_id": "9ace2b6431b7f1be",
    "country_abbreviation_link": "ca",
    "country_slug": "canada",
    "province": "Alberta",
    "province_abbreviation": "AB",
    "province_abbreviation_id": "aae1f05a0f89d2c7",
    "province_abbreviation_link": "ca/ab",
    "province_slug": "alberta"
  }
}

Filtering expansion data

When using expand to include datasets (listings, permits, assessments), expand filters control which expanded records are returned. They work independently of your main property filters and only affect expanded data.

Format: expand_[field]_[operator]=[value]

Listings filters

FilterDescriptionExample
expand_listing_date_gteListings after specified dateexpand_listing_date_gte=2024-01-01
expand_listing_date_gtListings strictly after dateexpand_listing_date_gt=2024-06-01
expand_listing_event_eqSpecific listing eventsexpand_listing_event_eq=Price decrease
expand_estimate_list_price_ltListing prices below amountexpand_estimate_list_price_lt=500000

Permits filters

Useful for finding properties with specific permits or renovations.

FilterDescriptionExample
expand_permit_application_date_gtePermits applied for after dateexpand_permit_application_date_gte=2023-01-01
expand_permit_content_regexSearch permit descriptions with regex"expand_permit_content_regex=(?i)\b(secondary|suite)\b"

Common permit search patterns

Search typeRegex pattern
Secondary suites"(?i)\b(secondary|suite)\b"
Renovations"(?i)\b(renovation|remodel)\b"
Kitchen and bathroom upgrades"(?i)\b(kitchen|bathroom)\b"
Outdoor improvements"(?i)\b(deck|patio|fence)\b"
Garage and parking"(?i)\b(garage|carport|parking)\b"

Assessments filters

FilterDescriptionExample
expand_assessment_year_eqAssessments for specific yearexpand_assessment_year_eq=2024
expand_assessment_value_gteAssessment value minimumsexpand_assessment_value_gte=500000
expand_assessment_value_lteAssessment value maximumsexpand_assessment_value_lte=800000

Filter expand match

filter_expand_match controls which properties return based on expanded data availability.

ValueBehaviorUse case
Default (no parameter)All properties, with or without expanded dataGeneral search with optional expansion
anyOnly properties with ANY requested expansion dataListing OR permit data
allOnly properties with ALL requested expansion dataListing AND permit data

If you only need properties that have a data set, and you do not need the expanded records themselves, filter on the matching boolean field instead: has_expand_listings, has_expand_listings_rent, has_expand_permits, or has_expand_assessments. For example, has_expand_listings_eq=true returns only properties with listing history. These are fast indexed filters on the property record itself, so they cost less and run faster than expanding a data set with filter_expand_match=all just to check for its presence.

Example usage

Recent price decreases
Request
Shell session
curl -X GET "https://api.houski.ca/properties?api_key=YOUR_API_KEY&country_abbreviation=ca&expand=listings&expand_listing_date_gt=2024-04-01&expand_listing_event_eq=Price decrease&filter_expand_match=all&page=1&province_abbreviation=ab&results_per_page=3&select=address_full,estimate_list_price"
TypeScript code
const houski_properties_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('country_abbreviation', 'ca');
    url.searchParams.set('expand', 'listings');
    url.searchParams.set('expand_listing_date_gt', '2024-04-01');
    url.searchParams.set('expand_listing_event_eq', 'Price decrease');
    url.searchParams.set('filter_expand_match', 'all');
    url.searchParams.set('page', '1');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('results_per_page', '3');
    url.searchParams.set('select', 'address_full,estimate_list_price');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.0,
  "data": [],
  "error": "Invalid column name: address_full",
  "pagination": {
    "current_page": 0,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 0
  },
  "price_quote": false,
  "result_total": 0,
  "time_ms": 783,
  "ui_info": {}
}
Secondary suite permits
Request
Shell session
curl -X GET "https://api.houski.ca/properties?api_key=YOUR_API_KEY&country_abbreviation=ca&expand=permits&expand_permit_application_date_gte=2023-01-01&expand_permit_content_regex=(?i)\b(secondary|suite)\b&filter_expand_match=all&page=1&province_abbreviation=ab&results_per_page=3&select=address_full,estimate_list_price"
TypeScript code
const houski_properties_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('country_abbreviation', 'ca');
    url.searchParams.set('expand', 'permits');
    url.searchParams.set('expand_permit_application_date_gte', '2023-01-01');
    url.searchParams.set('expand_permit_content_regex', '(?i)\b(secondary|suite)\b');
    url.searchParams.set('filter_expand_match', 'all');
    url.searchParams.set('page', '1');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('results_per_page', '3');
    url.searchParams.set('select', 'address_full,estimate_list_price');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.0,
  "data": [],
  "error": "Invalid column name: address_full",
  "pagination": {
    "current_page": 0,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 0
  },
  "price_quote": false,
  "result_total": 0,
  "time_ms": 700,
  "ui_info": {}
}
High assessments in 2024
Request
Shell session
curl -X GET "https://api.houski.ca/properties?api_key=YOUR_API_KEY&country_abbreviation=ca&expand=assessments&expand_assessment_value_gte=750000&expand_assessment_year_eq=2024&filter_expand_match=all&page=1&province_abbreviation=ab&results_per_page=3&select=address_full,estimate_list_price"
TypeScript code
const houski_properties_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('country_abbreviation', 'ca');
    url.searchParams.set('expand', 'assessments');
    url.searchParams.set('expand_assessment_value_gte', '750000');
    url.searchParams.set('expand_assessment_year_eq', '2024');
    url.searchParams.set('filter_expand_match', 'all');
    url.searchParams.set('page', '1');
    url.searchParams.set('province_abbreviation', 'ab');
    url.searchParams.set('results_per_page', '3');
    url.searchParams.set('select', 'address_full,estimate_list_price');

    const response = await fetch(url);
    const data = await response.json();

    return data;
}

(async () => {
let data: PropertiesResponse = await houski_properties_data();

// Log the response
console.log(data);
})();
Response
JSON
{
  "cache_hit": false,
  "cost_cents": 0.0,
  "data": [],
  "error": "Invalid column name: address_full",
  "pagination": {
    "current_page": 0,
    "has_next_page": false,
    "has_previous_page": false,
    "page_total": 0
  },
  "price_quote": false,
  "result_total": 0,
  "time_ms": 899,
  "ui_info": {}
}

Important notes

  • Without filter_expand_match, expand filters only narrow which records appear inside each expanded array. Every property still comes back, just with fewer attached records.
  • filter_expand_match=all keeps a property only when every dataset listed in expand= produces at least one matching record for it. Use this when you want properties that satisfy all conditions at once, like permit data AND listing data.
  • filter_expand_match=any keeps a property when at least one dataset in expand= produces a matching record. Use this for permit data OR listing data.
  • With expand=permits,listings, a property that matches on permits but has no matching listings is kept by any and dropped by all. A property with neither is dropped by both.
  • Prefix a regex with (?i) for case-insensitive matching, for example expand_permit_content_regex=(?i)kitchen matches Kitchen and KITCHEN.
  • Use "\b" word boundaries for whole-word matches, for example "\bsuite\b" matches suite but not suitable.
  • You can combine multiple expand_* filters in one request. Each filter applies to its own dataset, and filter_expand_match decides how the per-dataset hits combine into the final property list.