Skip to main content
POST
Search parcels by location, filters and quicklists
Every parcel inside a location that matches the filter groups and the quicklists, one parcel search row each. A row always carries the address and the assessor facts. It carries the owner, sale, tax, listing, valuation, financing, permit and history blocks when you ask for them.

Use it when

The property search screen, and any list built from parcel criteria instead of deeds. For example: absentee owners with equity, houses with a solar permit, parcels with a price cut since they were listed.

The request body

This is the one route that takes a POST, because the criteria nest. Send the same bearer key as everywhere else and Content-Type: application/json. This example asks for absentee owners of three-bedroom houses in one Scottsdale ZIP. The owners hold two or more properties and have most of their equity. The request sorts the best equity first and drops parcels on the market at the feed’s data end:
You must send location. Give at least one of zip, county_fips, point, bbox or property_id. The three geometries exclude each other, and you can narrow any one of them further with zip or county_fips. property_id searches around that parcel’s centre, and radius_miles beside it widens the circle. A point carries its own radius_miles. market restricts the search to one loaded market by code. A zip list alone chooses the markets. With no point, bbox or property_id, the API searches only the markets whose parcels carry those ZIP codes. A ZIP no market carries is 422 outside_coverage, and the problem names it in zips_unknown. The API combines the filter groups with AND, and every field inside a group the same way. The groups are general, building, lot, legal, owner, sale, tax, listing, valuation, financing, liens, permits and history. A range is { "min": ..., "max": ... } and both ends are inclusive. A parcel whose value is null does not pass a range. A quicklist is a named predicate over the same columns. quicklists keeps the parcels that match all of them. any_quicklists keeps those that match at least one. not_quicklists drops those that match any. Every quicklist and its predicate in words.

Datasets: what each row carries

datasets decides the blocks on a row. The default set is core, owner, sale, tax and listing. The rest are owner_profile, valuation, financing, permits, history and quicklists. A block you did not ask for is null. That null is not the same answer as “this parcel has none”, so read summary.datasets and do not guess from the row. For a name this API does not serve, the API answers 400 dataset_unavailable naming it. owner_profile adds owner.profile, the owner’s portfolio across every property BatchData links to them, read from the latest weekly delivery. It is what the owner.portfolio_count, portfolio_equity and portfolio_mortgages filters and the tired-landlord quicklist read.

Dated blocks

The valuation and financing datasets read a snapshot valued at meta.coverage[].parcel.financing.as_of and not current data. So do the valuation, financing and liens filter groups, the dated quicklists, and the equity_percent_desc, equity_percent_asc and avm_desc sorts.
  • summary.dated_filters names the parts of your request that read it, for example filters.valuation, quicklist high-equity, sort avm_desc. It is empty when nothing did.
  • meta.dated[] names the blocks on the wire, each with its as-of date and the reason.
  • With require_current: true, the API refuses such a request with 422 dated_refused and does not serve the snapshot.
Dated data explains the rule and what changes when the datasets return to the delivery.

Read the response

  • data[] is one parcel search row per parcel. Its id is the prop_ id every other property route takes.
  • The API sets distance_miles only when the location was a point or a property_id. In that case sort: "distance" is the default. Without one, the default order is id: market, then parcel id, the table’s primary key order. That order is stable and pages quickly over any location, but it means nothing.
  • meta.geometry is the location that ran. A point echoes as a radius. A property_id echoes as a radius around the parcel’s centroid, with property_id set, and with the default radius_miles when the body named none. A bbox echoes as the tuple, and a zip list alone echoes as a place. A county_fips list alone echoes nothing, and the API applies a zip or county_fips list beside a geometry as sent, without an echo. See the envelope.
  • quicklists[] on a row lists every quicklist the parcel matches, and it appears only when you ask for the quicklists dataset. The API computes it from the same expressions the filters use, so a row can never match a filter it does not report.
  • This host does not serve owner.names and owner.mailing: the owner block carries no such keys.
  • count_only: true answers with summary.total and no rows. A paged request leaves total null: follow page.next_cursor instead.

Gotchas

  • sort: "distance" needs a point or a property_id. Without one, the API answers 400 sort_requires_point. Every other sort puts null values last in both directions.
  • The history filter group, the history dataset and the history quicklists only answer inside the ZIP codes the history lake covers. meta.coverage[].parcel.history.zips and Counties, cities and ZIP codes list them. Elsewhere the block is null and those filters match nothing.
  • This host has no owner-name filter: it does not serve the owner names such a filter reads.
  • financing.lender_name_contains reads the lender, a business name, and is dated like the rest of the financing group. A lender’s profile, the Lender object, lists in names[] every spelling its instruments use. So you can widen a search on one spelling to the others.
  • financing.lender_id, in the same group, takes the registry id: len_ plus 12 hex, from a lender search, a ranking row or a parcel’s financing block. It matches the parcels whose last recorded mortgage’s lender carries that id, every spelling at once, over an index. For an id whose market has no lender registry, the API answers 400 validation_error naming the field. Since 0.17.0 the API follows an old lender_id to the current lender with meta.resolved_from. A retired one is 410 gone (Retired ids).
  • The private-lender-financed quicklist reads the same last mortgage. Its lender is a private party or a person (lender_class PRIVATE or INDIVIDUAL), or a hard-money lender of the registry (is_hard_money). See Quicklists.
  • A cursor is valid only for the market’s dataset_version and the body you sent. If you page across a refresh or with a changed body, the API answers 400 invalid_cursor.
  • For a market with no published parcel tables, the API answers 422 parcels_unavailable, and meta.coverage[].parcel is null there.
New construction near here: the search around a point and around a resolved address, with the listing, valuation and owner blocks read end to end.

Authorizations

Authorization
string
header
required

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

Body

application/json
location
object
required

Where to search: at least one of zip, county_fips, point, bbox or property_id. Send only one of point, bbox and property_id. You can combine that one with zip or county_fips.

filters
object

The filter groups. Every group and every field is optional. The API combines the groups with AND.

quicklists
string[]

Every listed quicklist must match (AND).

Required array length: 1 - 20 elements

A quicklist name, for example absentee-owner, free-and-clear, price-cut-listing, tired-landlord. The quicklists guide documents the list and every predicate. The API answers 400 quicklist_unavailable to an unavailable name.

Required string length: 1 - 64
any_quicklists
string[]

At least one listed quicklist must match (OR).

Required array length: 1 - 20 elements

A quicklist name, for example absentee-owner, free-and-clear, price-cut-listing, tired-landlord. The quicklists guide documents the list and every predicate. The API answers 400 quicklist_unavailable to an unavailable name.

Required string length: 1 - 64
not_quicklists
string[]

Keep parcels that match none of the listed quicklists.

Required array length: 1 - 20 elements

A quicklist name, for example absentee-owner, free-and-clear, price-cut-listing, tired-landlord. The quicklists guide documents the list and every predicate. The API answers 400 quicklist_unavailable to an unavailable name.

Required string length: 1 - 64
datasets
string[]

Which blocks each row carries: core, owner, owner_profile, sale, tax, listing, valuation, financing, permits, history, quicklists. Default: core, owner, sale, tax, listing. The valuation and financing blocks are dated (meta.dated[]). The API answers 400 dataset_unavailable to an unavailable name.

Required array length: 1 - 20 elements

Which blocks each row carries: core, owner, owner_profile, sale, tax, listing, valuation, financing, permits, history, quicklists. Default: core, owner, sale, tax, listing. The valuation and financing blocks are dated (meta.dated[]). The API answers 400 dataset_unavailable to an unavailable name.

Required string length: 1 - 32
require_current
boolean
default:false

With true, the API answers 422 dated_refused when the request filters on, sorts by or returns a dated block (valuation, financing, liens). Default false: the API serves dated blocks and stamps them in meta.dated[].

sort
enum<string>

Row order, null values last on every sort. distance: nearest first, the default with a point or a property_id. The sort id orders rows by market then id, the primary key order: stable and fast over any location, the default otherwise. The sorts last_sale_date_desc and last_sale_date_asc order by the last sale date, listing_price_asc and listing_price_desc by the list price. The sort last_owner_change_desc puts the history ZIP set first and the others last. The sorts equity_percent_desc, equity_percent_asc and avm_desc are dated (meta.dated[]). The sorts year_built_desc, year_built_asc, sqft_desc and last_permit_desc order by year built, the living area and the newest permit.

Available options:
distance,
id,
last_sale_date_desc,
last_sale_date_asc,
last_owner_change_desc,
equity_percent_desc,
equity_percent_asc,
listing_price_asc,
listing_price_desc,
avm_desc,
year_built_desc,
year_built_asc,
sqft_desc,
last_permit_desc
limit
integer
default:100

Page size, 1-500 (default 100).

Required range: 1 <= x <= 500
cursor
string

Opaque cursor from page.next_cursor of the previous page. A change of query, sort, weights or data version invalidates it (400 invalid_cursor). Then restart from page 1.

Required string length: 1 - 4096
count_only
boolean
default:false

True returns the count of matching parcels in summary.total and no rows.

Response

One page of parcels that match the location, filters and quicklists, in the requested sort, with the cursor for the next page. With count_only the response has no rows and carries summary.total.

One page of parcels that match the location, filters and quicklists, in the requested sort, with the cursor for the next page. With count_only the response has no rows and carries summary.total.

data
object[]
required

The rows of this page.

page
object
required

Pagination: the page size, the rows returned and the cursor for the next page. Paged lists carry no total. The summary block does.

meta
object
required

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

summary
object
required

What the request asked for and, on count_only, how many parcels match.