Last sale: dateSold, lastSoldPrice and calculated last sale

Four fields answer when the home last sold and for how much: Zillow's own dateSold and lastSoldPrice, and the last sale that PropertiesAPI reads from priceHistory in data.calculated.

FieldType
data.dateSoldstring (YYYY-MM-DD) or null
data.lastSoldPricenumber or null
data.calculated.last_sale_pricenumber or null
data.calculated.last_sale_datestring or null
data.calculated.change_since_last_sale_pctnumber or null
data.calculated.change_since_last_sale_basis"price", "zestimate" or null

Fields under data.calculated are calculated by PropertiesAPI from the other fields in the same response. They are not Zillow estimates.

What it means

lastSoldPrice is Zillow's last sold price for the home. It is often present on homes listed for sale again, where it is the price of the previous sale. dateSold is the date of the last sale and is filled for sold and off-market homes; on a home listed for sale it is usually null even when lastSoldPrice has a value.

calculated.last_sale_price and calculated.last_sale_date come from the most recent Sold event in priceHistory, so they work the same way for every status. When that sale's price wasn't disclosed, last_sale_price is null; an older sale's price is never used instead.

calculated.change_since_last_sale_pct compares a value now with that last sale price: the asking price for a home listed for sale, otherwise the Zestimate. change_since_last_sale_basis says which one was used.

Recorded example

Recorded lookup of a sold home
{
  "homeStatus": "RECENTLY_SOLD",
  "price": 385000,
  "dateSold": "2026-10-16",
  "lastSoldPrice": 385000,
  "calculated": {
    "last_sale_price": 385000,
    "last_sale_date": "2026-10-16"
  }
}
Recorded lookup of a home for sale
{
  "homeStatus": "FOR_SALE",
  "price": 487500,
  "dateSold": null,
  "lastSoldPrice": 415000,
  "calculated": {
    "last_sale_price": 415000,
    "last_sale_date": "2023-07-10",
    "change_since_last_sale_pct": 17.47,
    "change_since_last_sale_basis": "price"
  }
}
Recorded lookup of a sold home
{
  "homeStatus": "RECENTLY_SOLD",
  "price": null,
  "dateSold": "2026-02-24",
  "lastSoldPrice": null,
  "calculated": {
    "last_sale_price": null,
    "last_sale_date": "2026-02-24"
  }
}

Values are copied from recorded lookups of public Zillow pages, with the other fields of data left out. They show the shape and typical values, not today's data for these homes.

When it is null

  • The sale price isn't disclosed. A recorded sold home in New Mexico, a state without public sale prices, had a Sold event with no price, so lastSoldPrice and last_sale_price were null while the date was present.
  • The home has never sold in the period the page covers.
  • change_since_last_sale_pct is null when either side is missing: no disclosed last sale price, or no asking price and no Zestimate.

Read it in code

Send a Zillow link, a zpid or an address to GET /v1/zillow/property with your key in the x-api-key header, then read the field from data:

curl and jq
curl -sG "https://api.propertiesapi.com/v1/zillow/property" \
  -H "x-api-key: $PROPERTIESAPI_KEY" \
  --data-urlencode "url=https://www.zillow.com/homedetails/1704-Shelbourne-Dr-Austin-TX-78752/29412761_zpid/" \
  | jq '.data | {dateSold, lastSoldPrice, calculated: (.calculated | {last_sale_price, last_sale_date, change_since_last_sale_pct, change_since_last_sale_basis})}'
Python
import os
import requests

r = requests.get(
    "https://api.propertiesapi.com/v1/zillow/property",
    params={"url": "https://www.zillow.com/homedetails/1704-Shelbourne-Dr-Austin-TX-78752/29412761_zpid/"},
    headers={"x-api-key": os.environ["PROPERTIESAPI_KEY"]},
    timeout=120,
)
r.raise_for_status()
home = r.json()["data"]
c = home["calculated"]
print("last sale:", c["last_sale_date"], c["last_sale_price"])
if c["change_since_last_sale_pct"] is not None:
    print(f"change: {c['change_since_last_sale_pct']}% vs {c['change_since_last_sale_basis']}")
JavaScript (Node)
const url = "https://www.zillow.com/homedetails/1704-Shelbourne-Dr-Austin-TX-78752/29412761_zpid/";
const res = await fetch(
  `https://api.propertiesapi.com/v1/zillow/property?url=${encodeURIComponent(url)}`,
  { headers: { "x-api-key": process.env.PROPERTIESAPI_KEY } },
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data: home } = await res.json();
const c = home.calculated;
console.log("last sale:", c.last_sale_date, c.last_sale_price);
if (c.change_since_last_sale_pct != null) console.log(`change: ${c.change_since_last_sale_pct}% vs ${c.change_since_last_sale_basis}`);

Common mistakes

  • Reading dateSold on a listed home. It is usually null there; use calculated.last_sale_date.
  • Treating change_since_last_sale_pct as appreciation. When the basis is zestimate, one side is Zillow's estimate, not a sale.
  • Assuming price equals the sold price for every status. It only does for RECENTLY_SOLD homes with a disclosed price.

Zillow is a trademark of Zillow, Inc. PropertiesAPI is an independent service and is not affiliated with, endorsed by or sponsored by Zillow.

Last sale: dateSold, lastSoldPrice and calculated last sale questions

Why is lastSoldPrice null for a sold home?+

The sale price isn't public. Some states don't disclose sale prices, so Zillow shows the sale without a price.

Which field should I use for the last sale date?+

calculated.last_sale_date. It is read from priceHistory and works for listed, sold and off-market homes.

Is change_since_last_sale_pct adjusted for inflation?+

No. It is plain arithmetic on the two values, with no adjustment.