Developers
A small, free, read-only API for Singapore property reference data: MRT stations, schools, URA planning-area boundaries, the HDB concessionary loan rate, project search, and monthly price and volume trends by district, region, bedroom count or project. No key and no sign-up. Everything here is what a signed-out visitor to haio.sg already sees; signed-in and haio+ data is not part of it.
The machine-readable description is /openapi.json (OpenAPI 3.1). It is also listed in the API catalog and in llms.txt.
GET /api/hdb-loan-rate
The HDB concessionary housing loan interest rate for the current quarter, as posted by HDB (the CPF Ordinary Account rate + 0.10%). `ratePct` and `period` are null when no current rate is published.
curl -s "https://haio.sg/api/hdb-loan-rate"
GET /api/search/projects
Fuzzy name search over Singapore condo and landed projects, up to 12 results. Prices are asking prices of current listings (`priceBasis: asking`) or haio's median transacted price (`priceBasis: median`). With `q` absent, empty or shorter than 2 characters the endpoint returns a few featured projects instead of matches.
q: Project name or part of it, e.g. `marina`. Optional: absent or under 2 characters returns featured projects.curl -s "https://haio.sg/api/search/projects"
GET /api/map/stations
Every Singapore MRT/LRT station with coordinates and line codes, including announced future stations (`future: true`, with `openingYear` where known). `degraded: true` means the station table could not be read and the list is a fallback.
curl -s "https://haio.sg/api/map/stations"
GET /api/map/schools
Singapore primary, secondary, tertiary and international schools with coordinates and flags (GEP, SAP). Pass all four bounding-box parameters to limit the result to a map viewport. If any of the four is absent or not a number, the box is ignored and the whole island is returned (not a 400).
minLng: Bounding box west edge (longitude), e.g. 103.8. Use together with the other three.minLat: Bounding box south edge (latitude), e.g. 1.28.maxLng: Bounding box east edge (longitude), e.g. 103.9.maxLat: Bounding box north edge (latitude), e.g. 1.32.curl -s "https://haio.sg/api/map/schools"
GET /api/map/planning-areas
The 55 URA Master Plan 2025 planning areas as a GeoJSON FeatureCollection of MultiPolygons (simplified to about 11 m), with source and licence metadata (Singapore Open Data Licence). Planning areas are not postal districts.
curl -s "https://haio.sg/api/map/planning-areas"
GET /api/trendmaker/series
Median and average price per square foot, median and average price, and transaction count per month, quarter or year, for one segment: a bedroom count, a postal district (1-28), a market region (CCR, RCR, OCR), or one project by slug. Market segments count resale only; a project counts every sale type (`saleBasis`). Buckets with no sales are absent.
dim (required): Which kind of segment `key` names. One of: bedroom, district, region, project.key (required): The segment: 1-5 for bedroom, 1-28 for district, CCR/RCR/OCR for region, a project slug (from searchTrendProjects) for project.tenure: Property type. Default condo. One of: condo, landed, hdb.bucket: Time bucket. Default month. One of: month, quarter, year.bedroom: Optional exact bedroom filter on top of any segment (condo and hdb only). Anything but 1-5 is a 400.months: How many months back. Default 240. Anything but an integer from 1 to 480 is a 400.curl -s "https://haio.sg/api/trendmaker/series?dim=district&key=9"
GET /api/trendmaker/projects
Type-ahead over condo and HDB projects, up to 8 results, returning the `slug` getPriceSeries takes as `key` with `dim=project`. A `q` that is absent or shorter than 2 characters returns 200 with empty `results` (not a 400).
q: Project or block name; 2+ characters to get matches.tenure: Limit to one property type. One of: condo, hdb.curl -s "https://haio.sg/api/trendmaker/projects"
300 requests per 60 seconds per client IP, enforced at the Cloudflare edge. Above it the edge answers HTTP 429 with the JSON error rate_limited for 60 seconds, so on a 429 wait 60 seconds before retrying. Every response carries RateLimit-Policy: "api";q=300;w=60 (IETF draft RateLimit header fields). There is no remaining-quota header: the origin cannot see the edge counter, so it does not guess one. Automated clients that Cloudflare does not verify may be challenged on any request.
Errors are JSON with a stable, machine-readable code:
{
"error": {
"code": "not_found",
"message": "No API endpoint at this path.",
"hint": "Public API docs: https://haio.sg/docs/ (OpenAPI: https://haio.sg/openapi.json)."
}
}not_found (404) for an unknown /api/ path, method_not_allowed (405, with an Allow header) for anything but GET, HEAD or OPTIONS, invalid_parameter (400) for a bad query parameter, and 503 with Retry-After when a data source could not be read. The OpenAPI document lists every code per endpoint.
Transaction figures, loan rates and map layers come from public Singapore government sources (URA, HDB, OneMap, data.gov.sg). Asking prices in project search come from current listings. How the figures are computed is on the methodology page. Please cite haio.sg when you use them. Questions or a use case that needs more: contact us.