> ## Documentation Index
> Fetch the complete documentation index at: https://developers.investorlift.com/llms.txt
> Use this file to discover all available pages before exploring further.

# New construction near here

> New homes for sale near a point or an address, cheapest first: list price, days on market, valuation, and if the builder still holds title.

New construction is a parcel search with three quicklists on it. `new-construction` keeps the houses the assessor
dates to the data end's year or the one before. `active-listing` keeps the houses whose MLS record is Active.
`corporate-owned` keeps the houses a company still holds. On a house built last year, that company is nearly always
the builder.

Ask for the `listing` and `valuation` blocks and sort by list price. The answer is the builder inventory on the
market around a point. [Every quicklist and its predicate](/guides/concepts/quicklists).

## 1. Search around a point

Five miles around a point in Scottsdale, cheapest first:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.investorlift.com/v1/properties/search" \
    -H "Authorization: Bearer $GM_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "location": {
        "point": { "lat": 33.476917, "lng": -111.920385, "radius_miles": 5 }
      },
      "quicklists": ["active-listing", "new-construction", "corporate-owned"],
      "datasets": ["core", "owner", "listing", "valuation"],
      "sort": "listing_price_asc",
      "limit": 50
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://api.investorlift.com/v1/properties/search", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.GM_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      "location": {
        "point": { "lat": 33.476917, "lng": -111.920385, "radius_miles": 5 },
      },
      "quicklists": ["active-listing", "new-construction", "corporate-owned"],
      "datasets": ["core", "owner", "listing", "valuation"],
      "sort": "listing_price_asc",
      "limit": 50,
    }),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  const body = await res.json();
  ```

  ```python Python theme={null}
  import os

  import requests

  r = requests.post(
      "https://api.investorlift.com/v1/properties/search",
      json={
          "location": {
              "point": {"lat": 33.476917, "lng": -111.920385, "radius_miles": 5},
          },
          "quicklists": ["active-listing", "new-construction", "corporate-owned"],
          "datasets": ["core", "owner", "listing", "valuation"],
          "sort": "listing_price_asc",
          "limit": 50,
      },
      headers={"Authorization": f"Bearer {os.environ['GM_API_KEY']}"},
      timeout=30,
  )
  r.raise_for_status()
  body = r.json()
  ```
</CodeGroup>

The API combines the quicklists with AND, so every row is all three things at once. Move `corporate-owned` to
`not_quicklists`, and the list becomes the new builds that no company holds. Those owners are a person, a trust, or
an owner the roll does not classify. `filters.owner.kind: ["PERSON"]` beside the two quicklists means persons alone:
the new builds a buyer already took title to and listed again. Drop `corporate-owned`, and you get both.

The filter groups still apply beside the quicklists: `filters.building.beds` and `sqft` for the house,
`count_only: true` for the number alone.
[Search parcels](/api-reference/endpoints/properties-search) has every field.

## 2. Or start from an address

When the user types an address and does not drop a pin, resolve it first.
[`GET /v1/properties/resolve`](/api-reference/endpoints/properties-resolve) takes the street line with its ZIP (or
its city) and answers the `property_id`:

<CodeGroup>
  ```bash curl theme={null}
  curl "https://api.investorlift.com/v1/properties/resolve?address=7436%20E%20Virginia%20Ave&zip=85257" \
    -H "Authorization: Bearer $GM_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const params = new URLSearchParams({ address: "7436 E Virginia Ave", zip: "85257" });
  const res = await fetch(`https://api.investorlift.com/v1/properties/resolve?${params}`, {
    headers: { Authorization: `Bearer ${process.env.GM_API_KEY}` },
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  const body = await res.json();
  ```

  ```python Python theme={null}
  import os

  import requests

  r = requests.get(
      "https://api.investorlift.com/v1/properties/resolve",
      params={"address": "7436 E Virginia Ave", "zip": "85257"},
      headers={"Authorization": f"Bearer {os.environ['GM_API_KEY']}"},
      timeout=30,
  )
  r.raise_for_status()
  body = r.json()
  ```
</CodeGroup>

For a line that several parcels share (the units of one building), the API answers
[`422 ambiguous_address`](/guides/concepts/errors#ambiguous_address) with the units as `candidates[]`. Pass `unit` to
pick one. For an address that no parcel carries in that ZIP, the API answers a 404.

Then search around that parcel with `location.property_id`. `radius_miles` beside it sets the circle. Without it, the
search stays close to the parcel:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.investorlift.com/v1/properties/search" \
    -H "Authorization: Bearer $GM_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "location": {
        "property_id": "prop_e93c776c53354a88de4e58448a6bf21b",
        "radius_miles": 5
      },
      "quicklists": ["active-listing", "new-construction", "corporate-owned"],
      "datasets": ["core", "owner", "listing", "valuation"],
      "sort": "listing_price_asc",
      "limit": 50
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://api.investorlift.com/v1/properties/search", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.GM_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      "location": {
        "property_id": "prop_e93c776c53354a88de4e58448a6bf21b",
        "radius_miles": 5,
      },
      "quicklists": ["active-listing", "new-construction", "corporate-owned"],
      "datasets": ["core", "owner", "listing", "valuation"],
      "sort": "listing_price_asc",
      "limit": 50,
    }),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  const body = await res.json();
  ```

  ```python Python theme={null}
  import os

  import requests

  r = requests.post(
      "https://api.investorlift.com/v1/properties/search",
      json={
          "location": {
              "property_id": "prop_e93c776c53354a88de4e58448a6bf21b",
              "radius_miles": 5,
          },
          "quicklists": ["active-listing", "new-construction", "corporate-owned"],
          "datasets": ["core", "owner", "listing", "valuation"],
          "sort": "listing_price_asc",
          "limit": 50,
      },
      headers={"Authorization": f"Bearer {os.environ['GM_API_KEY']}"},
      timeout=30,
  )
  r.raise_for_status()
  body = r.json()
  ```
</CodeGroup>

Either way every row carries `distance_miles` from the centre.

## 3. Read the response

Each row is a [parcel search row](/api-reference/objects/parcel-search-row). These are the fields this list is about:

<ResponseField name="listing.price, listing.listed_on" type="mixed">
  The list price in whole dollars, and the listing date of the current record. Days on market is `listed_on` counted
  up to the listing feed's own as-of date, `meta.coverage[].listings_data_end`, never up to today. That date is later
  than the deed data end: the deeds and the listings arrive in one delivery, each with its own end. The feed ends on
  a date, and a house listed the week after it is not here yet.
</ResponseField>

<ResponseField name="building.year_built" type="integer">
  The assessor's year built. `new-construction` keeps the houses whose year is the data end's year or the one before.
  So at a data end in 2026 the list is the 2025 and 2026 builds.
</ResponseField>

<ResponseField name="owner.kind, owner.held_since" type="mixed">
  `ENTITY` is what `corporate-owned` matched: the builder, or the company that holds its lots, still on title.
  `held_since` is when that owner took the lot, usually well before `year_built`. This host does not serve
  `owner.names` or `owner.mailing`: the `owner` block carries no such keys.
</ResponseField>

<ResponseField name="valuation" type="object | null">
  The automated valuation with its range and its `as_of`: a dated snapshot, valued at the date `meta.dated[]` names
  for the block. That date can be earlier than the listing. Compare `estimated_value` with `listing.price`, and allow
  for the months between the two dates. A house the snapshot carries no valuation for has `valuation: null`.

  `summary.dated_filters` stays empty on this request, because nothing filtered or sorted on the snapshot.
  `meta.dated[]` names `valuation` on any page where a row carries one. A page of houses with no AVM, or a
  `count_only` request, lists nothing. [Dated data](/guides/concepts/dated-data).
</ResponseField>

## What this list is not

* **Where `meta.coverage[].parcel` carries a value.** The Phoenix and Seattle markets carry the parcel products
  today. For a point or a parcel in a market without them (Houston), the API answers
  [`422 parcels_unavailable`](/guides/concepts/errors#parcels_unavailable). `meta.coverage[].parcel` is null there,
  so you can tell in advance ([Coverage and freshness](/guides/concepts/coverage)). For a point outside every market,
  the API answers [`422 outside_coverage`](/guides/concepts/errors#outside_coverage).
* **MLS-listed only.** `active-listing` reads the MLS record the delivery carries for the parcel. A builder's
  inventory sold from the sales office without a listing is not here, and nothing marks it.
* **A standing house, as the assessor dates it.** `year_built` is the county roll's year on a parcel that exists. A
  to-be-built plan is not a parcel, and the API does not serve it. A spec home the roll still carries as a lot, with
  no year built yet, is not in the list until the roll updates. The lots themselves are the `vacant-lot` quicklist.
* **A dated valuation.** The AVM is a value as of the snapshot date, not the listing date and not today.
* **Days on market end at the listing feed's as-of date** (`meta.coverage[].listings_data_end`), not at the day you ask.

## Partners and staff

On the internal host, a key with the `contact` scope can narrow the list to one builder with the owner-name filter.
That filter reads the owner names that host serves:

```json theme={null}
{
  "filters": { "owner": { "name_contains_words": "LENNAR" } }
}
```

Every word must appear somewhere in an owner name, case-insensitive, and the filter needs a location. Beside
`active-listing` and `new-construction`, it is that builder's inventory on the market. Beside `vacant-lot`, it is the
lots the builder holds. The filter is not part of the public document at `api.investorlift.com`, whose owner group
has no such field. The internal host answers it, under the `contact` scope.


## Related topics

- [Search parcels](/api-reference/endpoints/properties-search.md)
- [Changelog](/changelog.md)
- [Rank the lenders of a market or a place](/api-reference/endpoints/lenders-list.md)
- [Rank nearby investors as buyers for a property](/api-reference/buyers/rank-nearby-investors-as-buyers-for-a-property.md)
- [The twenty-six tools](/mcp/tools.md)
