Skip to main content
GET
Read the comparable sales around a parcel
One parcel’s comparable sales, most similar first. The comps are every priced sale inside the radius and the window. Each comp’s parcel is within the bedroom, size and age tolerances of the subject. The route keeps one sale per parcel. The sales come from two sources the API already serves. Each comp also carries what the other products know about the parcel. Where the market has the parcel layer, the comp carries the layer’s cash proxy and the sale’s lender. Where the deal registry records an investor exit within 45 days of the sale, the comp carries the exit with its gross profit and hold.

Use it when

  • You have a parcel and want what houses like it sold for nearby. The parcel can come from resolve, a deal’s property.id or a search page.
  • You want the flip exits among the comps. A comp with a deal block is a sale whose purchase side the registry knows. So gross_profit and hold_days say what the rehab returned.
  • Use POST /v1/properties/search instead when you want the raw parcels around a point with your own filters and no similarity. Use the deal scan when you want investor deals, not every sale.

How a comp is chosen

  1. The subject. The parcel’s own facts set the tolerances: bedrooms, sqft and year_built. A fact the subject lacks cannot bound the comps. So the route does not apply that tolerance, and summary.tolerances carries null for it. The subject’s own last purchase appears on data.subject.last_sale for reference and is never a comp.
  2. The candidates. Every parcel of the subject’s market inside radius_miles, within beds_tolerance bedrooms, sqft_pct percent of the living area and year_tolerance years of the subject.
  3. The sales. From the deed registry, the route takes the parcel’s arm’s-length last purchase (source: DEED), the purchase the registry keeps for every parcel of every market. From the MLS, where the market has published listing tables (meta.coverage[].listings_data_end), it takes the parcel’s SOLD listing records (source: MLS), with days_on_market and list_price. A rental listing is never a sale. Both must fall inside the window, the last days_back days before the market’s data_end, and carry a price. An unpriced deed, as in a non-disclosure state, is not a comp.
  4. One sale per parcel. A parcel keeps its newest sale. When the deed and the MLS record the same sale, the MLS record wins if the deed recorded within 45 days of the close. So the comp carries the days on market.
  5. The ranking. similarity is one minus a weighted sum of five gaps, each over its own tolerance:
    • the distance over the radius (0.35)
    • the size gap over the size tolerance (0.25)
    • the bedroom gap over its tolerance (0.15)
    • the age gap over its tolerance (0.10)
    • the sale’s age over the window (0.15)
    A tolerance the route did not apply contributes nothing. The route orders the comps by it, then by distance, and cuts them at limit. A plan cap below it applies.
source picks the sources. mls in a market without listing tables answers 422 listings_unavailable. both serves only the deed side there, and summary.sources says so.

Read the response

  • data.subject: the parcel, its coordinates and the facts the tolerances read.
  • data.comps[]: the comp parcel and address, distance_miles, sold_on, sold_price, price_per_sqft, the building facts, source, the MLS fields, cash_sale_proxy, last_sale_lender, deal and similarity. cash_sale_proxy and last_sale_lender are null where the market has no parcel layer. They are also null where the layer’s last sale is a different sale, or where the market does not measure the proxy (meta.coverage[].parcel.sale_mortgage_measured false). deal is the exit recorded within 45 days of the sale, the nearest one, or null.
  • data.summary: n, n_deed, n_mls, median_price, median_price_per_sqft, price_per_sqft_value, min_price, max_price, the window, the sources read and the tolerances applied. price_per_sqft_value is the median price per square foot times the subject’s living area, in whole dollars. Every figure is over the comps returned, so a small limit narrows them.
  • meta.geometry echoes the subject as the radius that ran, with property_id the subject. The envelope describes it.
price_per_sqft_value is an indication, not a valuation. The route makes no time adjustment, no condition adjustment and no weighting by similarity. A valuation product is a later decision.

Credits

A call costs 2 credits, plus 1 for each comp parcel new to the account. A parcel already held by a search page or another comps call costs nothing again. See plans and limits.

Gotchas

  • The window ends at the market’s data_end, the last deed date, not today. summary.window_end says which day.
  • A parcel with no coordinates in any loaded market answers 404 not_found, like the other parcel routes.
  • The MLS side needs the listing tables. The deed side needs nothing beyond the market itself, so the route answers in every loaded market.
  • The largest request, five miles over three years in a dense area, runs for a second or two.

Authorizations

Authorization
string
header
required

API key from the developer console (starts with zpka_). Create one at https://developers.investorlift.com/get-a-key.

Path Parameters

property_id
string
required

Parcel id, prop_<32 hex>, from /v1/properties/resolve or any deal's property.id. The prefix is part of the id (400 invalid_id otherwise).

Required string length: 1 - 64

Query Parameters

radius_miles
number
default:1

Radius around the subject in miles, 0.1 to 5 (default 1).

Required range: 0.1 <= x <= 5
days_back
integer
default:180

Sales in the last N days before the market's data_end, 30 to 1095 (default 180).

Required range: 30 <= x <= 1095
beds_tolerance
integer
default:1

Bedrooms either side of the subject's, 0 to 5 (default 1). The API skips it when the subject's bedrooms are unknown.

Required range: 0 <= x <= 5
sqft_pct
integer
default:20

Percent either side of the subject's sqft, 0 to 50 (default 20). The API skips it when the sqft is unknown.

Required range: 0 <= x <= 50
year_tolerance
integer
default:15

Years either side of the subject's year_built, 0 to 50 (default 15). The API skips it when the year_built is unknown.

Required range: 0 <= x <= 50
source
enum<string>
default:both

With deed, the registry's arm's-length last purchases. With mls, SOLD listing records, and 422 listings_unavailable where the market has none. With both, the default, one sale per parcel, and the MLS record wins within 45 days of the deed.

Available options:
deed,
mls,
both
limit
integer
default:20

Comps to return, 1 to 50 (default 20), most similar first. A plan cap below this applies.

Required range: 1 <= x <= 50

Response

The comparable sales of one parcel.

The comparable sales of one parcel.

data
object
required

GET /v1/properties/{id}/comps: the subject, its comparable sales within the radius, the window and the tolerances, and the summary.

meta
object
required

Response metadata: when the API produced it, which markets it covers, and how fresh they are.