Instant expected sale price range, council valuation, sales history and rental estimate for any NZ address. Built to be fed straight from the Google Places API. Requires an API key.
https://estimate.remark.nz
All endpoints are GET, return JSON unless noted, and allow cross-origin requests from any page (Access-Control-Allow-Origin: *).
Every endpoint except this page and /health needs an API key. You will have been given one; if not, email oj@remark.nz. Send it in any one of these ways:
| Method | Example |
|---|---|
| Header (preferred) | Authorization: Bearer YOUR_KEY |
| Header (alternative) | X-API-Key: YOUR_KEY |
| Query string (Sheets, quick tests) | ?key=YOUR_KEY |
A missing or wrong key returns 401 {"error":"unauthorised"}. Keys are per person or per app, so keep yours out of public repos and client-side code where possible; if it leaks, ask for it to be rotated. If you must call from a browser, proxy through your own backend or accept that the key is visible.
Calls are logged (timestamp, key label, address queried, result, status, latency, caller IP and country) for support and fair-use monitoring. The key itself is never stored.
| Request | Returns |
|---|---|
/estimate?address=10 Hassing Place, Cromwell | Full property JSON (shape below). This is the one you normally want. |
/estimate?address=…&format=text | One plain-text line: address, low – high, mid, estimate date, CV, last sale. |
/estimate?address=…&raw=1 | Same JSON plus a raw object with the untouched upstream payloads. |
/estimate?address=…&nocache=1 | Skips the 6-hour edge cache and fetches fresh. |
/property/<property_id> | Same JSON, looked up by property ID (from a previous response). |
/search?q=10 Hassing | Address candidates only, with IDs and coordinates. No estimate. |
/batch?addresses=addr one|addr two | Up to 20 lookups in one call, separated by |. Returns {count, results:[…]}; a failed address carries an error instead of failing the whole batch. |
/health | {ok:true} |
URL-encode the address if your client does not do it for you (%20 for spaces, %2F for the slash in 3/15 Frankton Road). A suburb or town after the street helps disambiguate; a comma is optional.
Two ways, pick whichever your code already has in hand.
formatted_address as-ishttps://estimate.remark.nz/estimate?address=10 Hassing Place, Cromwell 9310, New Zealand
The postcode and , New Zealand are stripped automatically before matching.
address_components types as parametershttps://estimate.remark.nz/estimate?subpremise=3&street_number=15&route=Frankton Road&sublocality=Queenstown&locality=Queenstown
| Google component type | Query parameter |
|---|---|
subpremise (unit / flat) | subpremise |
street_number | street_number |
route (street name) | route |
sublocality / sublocality_level_1 / neighborhood (suburb) | sublocality |
locality (town / city) | locality |
// JavaScript: from a Places Autocomplete place object
const get = t => (place.address_components.find(c => c.types.includes(t)) || {}).long_name || '';
const params = new URLSearchParams({
subpremise: get('subpremise'), street_number: get('street_number'), route: get('route'),
sublocality: get('sublocality') || get('sublocality_level_1') || get('neighborhood'),
locality: get('locality'),
});
const data = await (await fetch('https://estimate.remark.nz/estimate?' + params, { headers: { Authorization: 'Bearer YOUR_KEY' } })).json();
If both address and components are sent, address wins. Restrict Places Autocomplete to componentRestrictions: { country: 'nz' } and types: ['address'] for best results.
curl -H "Authorization: Bearer YOUR_KEY" "https://estimate.remark.nz/estimate?address=10%20Hassing%20Place%2C%20Cromwell&format=text"
curl -H "Authorization: Bearer YOUR_KEY" "https://estimate.remark.nz/estimate?address=10%20Hassing%20Place%2C%20Cromwell" | jq .estimate
$h = @{ Authorization = "Bearer YOUR_KEY" }
$r = Invoke-RestMethod "https://estimate.remark.nz/estimate?address=10 Hassing Place, Cromwell" -Headers $h
$r.estimate # low / mid / high / date
$r.council_valuation.capital
Invoke-RestMethod "https://estimate.remark.nz/estimate?address=10 Hassing Place, Cromwell&format=text" -Headers $h
import requests
H = {"Authorization": "Bearer YOUR_KEY"}
r = requests.get("https://estimate.remark.nz/estimate", params={"address": "10 Hassing Place, Cromwell"}, headers=H).json()
if "error" in r:
print(r["error"])
else:
e = r["estimate"]
print(r["matched_address"], e["low"], e["mid"], e["high"], e["date"])
const res = await fetch("https://estimate.remark.nz/estimate?address=" + encodeURIComponent("10 Hassing Place, Cromwell"), {
headers: { Authorization: "Bearer YOUR_KEY" },
});
const data = await res.json();
if (data.error) console.log(data.error);
else console.log(data.matched_address, data.estimate.low, data.estimate.high);
=IMPORTDATA("https://estimate.remark.nz/estimate?address="&ENCODEURL(A2)&"&format=text&key=YOUR_KEY")
Sheets caches IMPORTDATA results for a while; the text format keeps the result to one cell.
{
"query": "10 Hassing Place, Cromwell",
"matched_address": "10 Hassing Place, Cromwell", // always check this
"property_id": "10a2cfb4-3f55-426b-8e7b-a4e268b01121",
"lat": -45.0412, "lng": 169.1841,
"estimate": { // null if no estimate exists for the property
"low": 810000, "mid": 865000, "high": 915000,
"date": "2026-09-03", // when the estimate was last revised
"display": "$810K – $915K",
"model": "v3 (2025-01-22)"
},
"rental_estimate": { "low_per_week": 880, "high_per_week": 1480, "date": "2026-06-02", "gross_yield": "7.1%" },
"council_valuation": {
"capital": 830000, "land": 480000, "improvements": 350000, "date": "2025-09-01",
"history": [ { "date": "2025-09-01", "capital": 830000, … }, { "date": "2022-10-01", … } ]
},
"last_sale": { "price": 383000, "date": "2014-08-13", "type": "S11", "council_confirmed": true },
"sales": [ …every recorded sale, newest first… ],
"property": {
"bedrooms": 3, "bathrooms": 2, "car_spaces": 1,
"floor_area_m2": 185, "land_area_m2": 644, "decade_built": "1970",
"ownership": "Freehold", "land_use": "house",
"legal_description": "LOT 5 DP 17109", "title": "OT11C/1270", "valuation_ref": "28506/19100",
"suburb": "Cromwell", "city": "Cromwell"
},
"listing": { // only present while the property is on the market
"headline": "Space, Flexibility and Greenway Living",
"display_price": "Deadline sale by 24 Sept",
"agency": "PGG Wrightson",
"trademe_ids": ["6112545398"],
"cover_image": "https://…"
},
"trademe_url": "https://www.trademe.co.nz/a/property/residential/sale/listing/6112545398",
"candidates": [ …other address matches, if the top pick looks wrong… ],
"fetched_at": "2026-09-13T01:02:03.000Z"
}
All money values are whole NZ dollars. Areas are square metres. Dates are YYYY-MM-DD.
| Status | Meaning |
|---|---|
| 400 | Missing address / q / addresses parameter. |
| 401 | Missing or invalid API key. See Authentication above. |
| 404 | no street address matched that query — nothing in the index looked like a street address. Or address found but no property record exists for it — typical for commercial sites, brand-new subdivisions and some units; the response includes candidates. |
| 502 | Upstream data source unreachable. Retry later. |
Error bodies are JSON: {"query": "…", "error": "…", "status": 404}. Success bodies never contain an error key, so if (data.error) is a safe check.
matched_address. Address matching is fuzzy and can drop a unit number (3/15 Frankton Road may come back as 15 Frankton Road) or pick a same-named street in another town if you omit the suburb.estimate.date). Each successful lookup is cached for 6 hours; &nocache=1 bypasses that. Response header x-cache says HIT or MISS.Questions? Contact oj@remark.nz for support.