curl --request GET \
--url https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson \
--header 'X-API-Key: <api-key>'import requests
url = "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"statusCode": 200,
"data": {
"data": [
{
"wardId": 34,
"wardName": "Phú Thuận",
"areaName": "Khu vực II",
"area": "Khu vực II",
"avgPriceResidentialVt1": 52639.39,
"avgPriceResidentialVt2": null,
"avgPriceResidentialVt3": null,
"avgPriceResidentialVt4": null,
"minPriceResidential": 36500,
"maxPriceResidential": 83400,
"roadCount": 33,
"unit": "k_vnd",
"coordinates": [
[
[
106.751906,
10.7051207
],
[
106.751422,
10.7074537
]
]
]
}
],
"meta": {
"availableYears": [
2025
],
"latestYear": 2025,
"unit": "k_vnd",
"usdRate": 0.00003966381630640323
}
},
"meta": {
"timestamp": "2026-09-16T07:25:17.448Z"
}
}{
"success": false,
"statusCode": 400,
"error": {
"code": "INTERNAL_ERROR",
"message": [
"Invalid type parameter. Must be \"gdp\", \"cpi\", \"pce\", or \"import-export\"",
"Type parameter is required"
]
},
"meta": {
"error": "Bad Request",
"statusCode": 400,
"timestamp": "2026-06-19T08:34:08.081Z"
}
}{
"success": false,
"statusCode": 401,
"error": {
"code": "API_KEY_INVALID",
"message": "Invalid API key"
},
"meta": {
"timestamp": "2026-06-19T08:34:08.023Z"
}
}{
"success": false,
"statusCode": 403,
"error": {
"code": "FEATURE_NOT_IN_PLAN",
"message": "Your plan does not include this feature"
},
"meta": {
"timestamp": "2026-06-19T08:34:08.100Z"
}
}{
"success": false,
"statusCode": 404,
"error": {
"code": "INTERNAL_ERROR",
"message": "Commodity 'zzz-nope' not found"
},
"meta": {
"error": "Not Found",
"statusCode": 404,
"timestamp": "2026-06-19T08:34:08.205Z"
}
}{
"success": false,
"statusCode": 429,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded"
},
"meta": {
"timestamp": "2026-06-19T08:34:09.000Z"
}
}{
"success": false,
"statusCode": 500,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error"
},
"meta": {
"timestamp": "2026-06-19T08:34:10.000Z"
}
}Ward choropleth GeoJSON for one province
Boundary geometry for every ward in provinceId, carrying residential land
prices. meta.bands holds percentile colour thresholds (non-Vietnam markets
only) — when absent, the client falls back to its own fixed bands. meta.unit
says how to format prices, and meta.usdRate is the USD value of one unit of
local currency.
Payloads are large — a single province can run to several megabytes. Use the province-prices endpoint when a dot per province is enough.
curl --request GET \
--url https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson \
--header 'X-API-Key: <api-key>'import requests
url = "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-v2.hayinsights.com/openapi/v1/real-estate/{country}/provinces/{provinceId}/geojson")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"statusCode": 200,
"data": {
"data": [
{
"wardId": 34,
"wardName": "Phú Thuận",
"areaName": "Khu vực II",
"area": "Khu vực II",
"avgPriceResidentialVt1": 52639.39,
"avgPriceResidentialVt2": null,
"avgPriceResidentialVt3": null,
"avgPriceResidentialVt4": null,
"minPriceResidential": 36500,
"maxPriceResidential": 83400,
"roadCount": 33,
"unit": "k_vnd",
"coordinates": [
[
[
106.751906,
10.7051207
],
[
106.751422,
10.7074537
]
]
]
}
],
"meta": {
"availableYears": [
2025
],
"latestYear": 2025,
"unit": "k_vnd",
"usdRate": 0.00003966381630640323
}
},
"meta": {
"timestamp": "2026-09-16T07:25:17.448Z"
}
}{
"success": false,
"statusCode": 400,
"error": {
"code": "INTERNAL_ERROR",
"message": [
"Invalid type parameter. Must be \"gdp\", \"cpi\", \"pce\", or \"import-export\"",
"Type parameter is required"
]
},
"meta": {
"error": "Bad Request",
"statusCode": 400,
"timestamp": "2026-06-19T08:34:08.081Z"
}
}{
"success": false,
"statusCode": 401,
"error": {
"code": "API_KEY_INVALID",
"message": "Invalid API key"
},
"meta": {
"timestamp": "2026-06-19T08:34:08.023Z"
}
}{
"success": false,
"statusCode": 403,
"error": {
"code": "FEATURE_NOT_IN_PLAN",
"message": "Your plan does not include this feature"
},
"meta": {
"timestamp": "2026-06-19T08:34:08.100Z"
}
}{
"success": false,
"statusCode": 404,
"error": {
"code": "INTERNAL_ERROR",
"message": "Commodity 'zzz-nope' not found"
},
"meta": {
"error": "Not Found",
"statusCode": 404,
"timestamp": "2026-06-19T08:34:08.205Z"
}
}{
"success": false,
"statusCode": 429,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded"
},
"meta": {
"timestamp": "2026-06-19T08:34:09.000Z"
}
}{
"success": false,
"statusCode": 500,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error"
},
"meta": {
"timestamp": "2026-06-19T08:34:10.000Z"
}
}Authorizations
Your HayInsights API key (prefixed apk_). Create and manage keys in the
HayInsights dashboard (Account → API keys).
Send it in the X-API-Key header on every request to /openapi/v1/*.
Which data domains you may access and your request quota are both governed by
the subscription plan attached to the key — see the Plans & features and
Rate limits guides.
Path Parameters
Country code.
vn, jp "vn"
Province ID, from the provinces endpoint.
1
Query Parameters
Language for name fields.
vi, en Response
Ward boundaries and prices for the province.
Standard success envelope shared by every endpoint. Each operation's response
wrapper extends this (via allOf) and adds a typed data property.
Always true for a successful response.
true
Mirrors the HTTP status code.
200
Ward features plus the colour thresholds for this province. Note the
nesting: the features are at data.data, and data.meta is the route's own
metadata, distinct from the envelope's meta.
Show child attributes
Show child attributes
Response metadata. Always present; timestamp is the server time the response was generated.
Show child attributes
Show child attributes