Skip to main content
GET
List every deal of one investor across the market
What the investor bought, what the investor sold and what the investor made, across the whole market. A two mile radius shows about 5% of an active flipper’s activity. This route shows all of it.

Use it when

The drawer’s Bought / Sold / Profit table, or a map of an investor’s whole footprint. For the investor’s deals inside one area, use GET /v1/deals?investor_id= instead.

The role parameter

With seller or any, the date sort uses the resale date. If the investor never resold the deal, the sort uses the purchase date. So “most recent first” means the most recent event of this investor. On those rows investor is the buyer, often a household without an id. This host does not serve its name and state: both are null, with contact_redacted: true.

Read the response

  • distance_miles is null: this endpoint takes no geometry.
  • listing_status= keeps holdings by the MLS listing on the parcel, before the API cuts the page. on_market keeps a holding whose listing is ACTIVE or PENDING and not a rental. active, pending, sold and off_market are the raw listing.status, rentals included. none keeps a holding with no listing record. A comma list keeps a row when any value matches. Rows that are not holdings never match.
  • The drawer’s “on the market” pane is role=buyer&listing_status=on_market: every listed holding in one bounded page. The investor’s own inventory is the rows with listing.counted true. The rest are rows a prior owner listed (owner_test FAILED), or undated rows.
  • The response fills owner only on confirmed current holdings, and it carries the contact fields.
  • short_term_rental, on hold rows, is the parcel’s city licence record. str_status=LICENSED,PENDING keeps the holdings with a licence or an application at the roll’s as-of date (meta.coverage[].str_as_of). EXPIRED keeps the lapsed ones. str_business_use=true keeps the holdings whose licence is a business use attributed to this investor. The rows that last filter keeps are exactly the holdings the profile’s str.n_holdings counts.

Gotchas

  • The largest investors have more than 25,000 rows, so page to the end. This route has no CSV on api.investorlift.com. See Give me a spreadsheet. The one public CSV is the loans of a lender.
  • The API follows old ids as on the profile route: 200 with meta.resolved_from, 410 gone for a retired id, 404 for an id never issued.
  • A market with no listing tables answers 422 listings_unavailable for listing_status. A market with no short-term rental tables answers 422 str_unavailable for the str_ filters. In such a market, an empty page reads as “nothing listed”. So the API answers an error, not zero rows.

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

id
string
required

Investor id, inv_ followed by 12 hex characters, for example inv_abaf618f44a3. The API follows an old id from an earlier data refresh to the current investor (meta.resolved_from). It answers 410 for a retired id.

Query Parameters

market
string

Market code, for example phx. The loaded markets are in meta.coverage[]. Send it only when the id exists in more than one loaded market. 400 market_required says so. Otherwise the API uses the id's own market.

Required string length: 1 - 16
role
enum<string>
default:buyer

Which side of the deals to list. With buyer (the default) the list holds the purchases, holdings and resales where the investor was the buyer. With seller it holds the deals where the investor sold to the buyer named on the row. With any it holds the buyer, seller and end-buyer rows together, each deal once.

Available options:
buyer,
seller,
any
kind
enum<string>[]

Deal kinds to keep (comma list or repeated key). Default: every kind.

Minimum array length: 1

What occurred on the deal. flip: bought, fixed and resold within about a year at a profit-shaped margin; wholesale: bought and resold in under 30 days, or a chain resale or round trip within 90 (a double close); hold: bought and still owned at data_end, a confirmed holding or an un-resold purchase; long_hold: held over a year, then sold (a landlord exit); other: an investor resale that is not a clean flip. Reasons: a loss, a thin margin, a builder's new home resold, an iBuyer resale, or a chain resale of 3 to 12 months; build: a registered investor bought a lot or teardown, built a house on it during the hold, then sold. A builder's finished home bought and resold is other. On data built after 2026-09-17 the kind keeps only a single-family parcel held over 90 days, its house complete by the sale year. A market built on or before 2026-09-17 (meta.coverage[].loaded_at) counts every lot-to-build resale by a registered investor as a build until its next build.

Available options:
flip,
wholesale,
hold,
long_hold,
other,
build
listing_status
enum<string>[]

Keep only holdings whose MLS listing matches one of these values (comma list or repeated key). The value on_market keeps the holdings listed at listings_data_end: ACTIVE or PENDING and not a rental. The values active, pending, sold and off_market match the record's status, and none keeps holdings with no record. The API applies the filter before it cuts the page, so role=buyer&listing_status=on_market is every listed holding in one page. It answers 422 listings_unavailable when the market has no listing tables (meta.coverage[].listings_data_end null). Default: every row.

Minimum array length: 1

What the MLS listing on a holding must be for the API to keep the row. With on_market, ACTIVE or PENDING and not a rental: the rows whose listing block says on_market, before the ownership test. So the counted ones among them are those with counted true. With active, pending, sold or off_market, the block's status, rentals included, and with none, a holding with no listing record at all. The API keeps a row when any of the values matches. Rows that are not holdings (flips, wholesales, resold purchases) carry no listing and never match.

Available options:
on_market,
active,
pending,
sold,
off_market,
none
source
enum<string>

The value investorlift keeps only deals whose closing deed closed an Investorlift listing. Those rows carry a wholesale_listing block: the houses this investor bought off Investorlift. The API applies the filter before it cuts the page. It answers 422 wholesale_unavailable when the market has no wholesale tables (meta.coverage[].wholesale_as_of null). Default: every row.

Available options:
investorlift
str_status
enum<string>[]

Keep only holdings whose short-term rental status matches one of the values (comma list or repeated key): LICENSED, PENDING or EXPIRED, the block's status. The API serves NONE on rows, but NONE is not filterable in this release (400 for the value). The API applies the filter before it cuts the page. Rows that are not holdings never match. 422 str_unavailable when a market in play has no short-term rental tables (meta.coverage[].str_as_of null). Default: every row.

Minimum array length: 1

The short-term rental status a holding must carry for the API to keep the row: the block's status. LICENSED means an active licence or permit, PENDING an application on file with none active, and EXPIRED every record on the parcel inactive. The API keeps a row when any of the values matches. Rows that are not holdings (flips, wholesales, resold purchases) carry no block and never match. The API serves NONE on rows, but NONE is not filterable in this release (400 for the value).

Available options:
LICENSED,
PENDING,
EXPIRED
str_business_use
enum<string>

With true, keep only hold rows whose licence is a business use attributed to the current owner: the UI's short-term rental deal type. With false, keep only hold rows whose licence is not one: a person's own home with a casita permit, or a prior owner's permit. Without str_status the test covers LICENSED and PENDING records, the only ones where business_use is true. Combine it with str_status to widen or narrow. The API applies the filter before it cuts the page. Rows that are not holdings never match. 422 str_unavailable when a market in play has no short-term rental tables (meta.coverage[].str_as_of null). Default: every row.

Available options:
true,
false
sort
enum<string>
default:date_desc

The values date_desc (the default) and date_asc sort on the purchase date for role=buyer. For role=seller and role=any they sort on the resale date, or on the purchase date for a deal with no resale. Null dates come last in both directions. The values price_desc and price_asc sort on the bought price, nulls last.

Available options:
date_desc,
date_asc,
price_desc,
price_asc
limit
integer
default:500

Page size, 1-500 (default 500).

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

Response

The investor's deals across the whole market, one page, in the requested sort.

The investor's deals across the whole market, one page, in the requested sort.

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.