← back to AbramsOS
lib/home-value.js
38 lines
// home-value.js — optional automatic property valuation via RentCast AVM.
//
// One-key-from-working: set RENTCAST_API_KEY (free tier = 50 lookups/mo at
// rentcast.io) and the Assets "Estimate" button fills current_value from RentCast's
// automated valuation model. No key → graceful "not configured" (never fabricates a value).
// Zillow's Zestimate API is retired; RentCast is the current no-cost option.
function isConfigured() {
return Boolean(process.env.RENTCAST_API_KEY);
}
async function estimateValue(address) {
const key = process.env.RENTCAST_API_KEY;
if (!key) return { ok: false, reason: 'no_key' };
if (!address) return { ok: false, reason: 'no_address' };
const url = `https://api.rentcast.io/v1/avm/value?address=${encodeURIComponent(address)}`;
try {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), 12000);
const r = await fetch(url, { headers: { 'X-Api-Key': key, accept: 'application/json' }, signal: ctrl.signal });
clearTimeout(t);
if (!r.ok) return { ok: false, reason: `http_${r.status}` };
const j = await r.json();
const price = Number(j.price ?? j.value);
if (!Number.isFinite(price)) return { ok: false, reason: 'no_price' };
return {
ok: true,
price: Math.round(price),
low: Number.isFinite(Number(j.priceRangeLow)) ? Math.round(Number(j.priceRangeLow)) : null,
high: Number.isFinite(Number(j.priceRangeHigh)) ? Math.round(Number(j.priceRangeHigh)) : null,
};
} catch (e) {
return { ok: false, reason: e.message };
}
}
module.exports = { isConfigured, estimateValue };