Investorlift DS
  • Getting started
  • The contract
  • Errors
  • API reference
Information
MCP
    MCP Endpointpost
Scoring
    Score a property addresspostScore a batch of property addressespostCheck a request without spendingpost
History
    Read a property’s recorded transaction historypost
Schemas
Investorlift Data Services API
Investorlift Data Services API

History


Read a property’s recorded transaction history

POST
https://api.investorliftdata.com
/v1/history

Returns one property’s recorded transaction story: the chain of title newest first, and any detected buy/sell flips.

Input is exactly one of address or property_id. Send an address for your own lead, or send the property_id of a comparable property taken straight from a Scored Call answer. A request carrying both, or neither, is rejected with 400 before any Credits are consumed.

This is a Metered Call. The Credit cost is resolved by the Ledger inside the same transaction that records the Spend; it is never sent by the client. The Spend is committed before the county records are read, so a call that reaches them has already been paid for, and a call that fails before the Spend consumes nothing. The History Call page in the Docs states what this endpoint costs.

A property with nothing on file is a chargeable answer. If the county records are available and hold no recorded transactions for this property, the call returns 200 with an empty transactions array and a plain statement saying the records were checked and nothing was on file — and the Metered Call is charged normally, because “we checked; nothing on file” is an answer rather than a failure. If the records themselves could not be read, that is a failure: the call returns a 5xx and the Credit is returned to the Account as a Refund.

Every category in the answer is a derived conclusion, not a statement made by the county. The transaction categories and buyer types are this API’s reading of the recorded documents, and an unrecognised record is reported as unknown rather than guessed at.

No names appear. Buyer and seller names in the underlying records are used only to reach a conclusion — that a buyer was a company, for instance — and never appear in the response.

Send an Idempotency-Key to make a retry safe. A key is replayed only when the earlier call succeeded and its response was stored. If the earlier call was refunded, ended without a stored response, or is older than the replay window, the key is taken over: the new request runs and is charged, whatever body it carries. Otherwise the same key sent with a different body is refused as a conflict, and nothing is charged for it.

Read a property’s recorded transaction history › Headers

Authorization
​string · required

The Authorization header is used to authenticate with the API using your API key. Value is of the format Bearer YOUR_KEY_HERE.

Idempotency-Key
​string

An opaque string of your choosing that makes retrying this request safe. Optional — a request without one is never an error, and is charged per call. Reusing a key with the same body within 24 hours returns the stored result and is not charged again, but only when the earlier call succeeded and its response was stored. Only successful responses are stored: if the earlier call was refunded or ended without a stored response, the key is taken over and the new request runs and is charged, whatever body it carries. Otherwise reusing a key with a different body is rejected as a conflict, and nothing is charged for it. The replay window is 24 hours, measured from the request that produced the stored result; after 24 hours the same key is a new request and is charged. If an identical request carrying the same key is still in flight, this one waits briefly for that result; if it does not arrive, the response is 409 with a Retry-After header and no Credits are consumed for it. Keys are scoped to your Account — one Account never replays another Account's result. POST /v1/validate ignores this header: it is not a Metered Call, so there is nothing to replay.

Read a property’s recorded transaction history › Request Body

**Exactly one of `address` or `property_id`.** A body carrying both, or neither, is rejected with 400 before any Credits are consumed. Presence is what counts: sending an empty `address` alongside a real `property_id` still counts as sending both, and is rejected — rather than have this API quietly choose which of the two you meant. This endpoint accepts no other members and no request options. `condition`, `rehab_budget`, `target_margin` and `overrides` belong to the Scored Call and are rejected here.
HistoryCallRequest
address
​string · minLength: 1

The full street address of the property whose history you want.

Example: 742 Evergreen Terrace, Springfield, IL 62701
property_id
​string · pattern: ^[0-9a-f]{32}$

A property identifier taken from a Scored Call answer — the subject's own, or any comparable property's. 32 lowercase hexadecimal characters. On this arm no address matching is performed: the record is looked up directly, and subject_property reads back exactly which record answered. Identifiers are looked up in the same identifier space the Scored Call publishes them in.

Example: a1b2c3d4e5f60718293a4b5c6d7e8f90

Read a property’s recorded transaction history › Responses

The History Call succeeded. This was a Metered Call, so Credits were consumed from the Account’s Balance, as resolved by the Ledger. An empty transactions array here means the records were checked and nothing was on file — that is an answer, and it is charged.

One property's recorded transaction story, built by Investorlift Data Services member by member; no upstream response is ever passed through. Every response also carries the Trace ID as the `trace-id` header. **The member set below is closed.** This endpoint publishes the property readback, the chain of title, any detected flips, the seller's position, recorded permit work, the current listing story and an at-a-glance summary — and nothing else. **A chapter that could establish nothing says so, rather than publishing a zero.** You will not find an empty `permits` array standing alone: the permits chapter carries `coverage_known` and a plain sentence beside its list, because an empty array on its own would state that this property has no permits when in fact no permit record could be retrieved — and those two facts are indistinguishable to us. `seller_position` carries `recorded_loan_available` for the same reason, and publishes no payoff member and no equity member at all rather than a null one. **An empty `transactions` array is a verified emptiness and it is charged.** It means the county records were available, were read, and hold no recorded transactions for this property. `records_statement` says so in words. If the records could not be read at all, this endpoint does not return 200 — it returns an error and the Credit is Refunded. **Every category in this answer is a derived conclusion, not an asserted fact.** Transaction categories, buyer types, seller postures, permit statuses and listing states are this API's reading of the recorded documents. An unrecognised record is reported as `unknown` rather than guessed at.
HistoryCallResponse
​object · required

The property this history describes, read back to you. This is the same block a Scored Call publishes — same members, same provenance labels, same honesty members — so a comparable property you looked up by property_id can never be silently narrated as a different house. On this endpoint every fact carries the source county-record: there is no what-if on a History Call, so no fact here is ever caller-stated.

Example: {"property_id":"a1b2c3d4e5f60718293a4b5c6d7e8f90","address":{"street":"2030 Lone Rock Dr","city":"kingwood","state":"TX","zip":"77345-1234"},"facts":{"square_feet":{"value":2416,"source":"county-record"},"bedrooms":{"value":4,"source":"county-record"},"bathrooms":{"value":2.5,"source":"county-record"},"partial_bathrooms":{"value":1,"source":"county-record"},"year_built":{"value":1998,"source":"county-record"},"lot_size_square_feet":{"value":8276,"source":"county-record"},"property_type":{"value":"Single Family","source":"county-record"}},"address_quality":{"code":"zip-confirmed","statement":"The house number matched exactly one county record, and that record sits in the same ZIP code your address resolved to. Check the address and property facts in this block before relying on these numbers."},"outside_model_range":{"flagged":false,"reasons":[]}}
records_statement
​string · required

A plain sentence stating what was checked and what was found, suitable to show a colleague. Fixed wording selected from a table — never assembled, and it never repeats the address or identifier you submitted. It makes no claim about your Balance: what a call cost is recorded on the Ledger, not asserted in the answer.

​object[] · required

The chain of title, newest first. Events the record leaves undated cannot be ordered and are listed last rather than presented as the oldest. A transaction that appears in more than one county record set is published once.

​object[] · required

Detected buy/sell pairs, newest first. A pair is reported only when both legs carry a real market price — a leg recorded at a nominal price would make the percentage gain a fabricated number — and only when neither leg was a family transfer.

​object · required

How long the current owner has held title, and a plain statement of what cannot be told you about their loan.

There is no payoff member and no equity member here, and there is no null one either. This chapter was specified to publish a payoff range and an equity range derived from the loan recorded at the property's last sale. We went looking for that record — through the property data service's full published route list, through thirty-one candidate record-set names, and by matching every one of 579 distinct column names it returns across a sample of properties — and no per-property loan, mortgage or lien record is available to us: no loan amount, no loan date, no lender, no rate, no maturity and no payoff.

Nothing is inferred from that absence. This answer never says a property is owned free and clear and never says a purchase was made in cash. A missing record means we did not find one to look at; it does not mean no loan exists.

Were such a record to become available, a payoff derived from the loan recorded at the last sale would still be an estimate about the past — refinances after that sale are invisible to the public record — which is why it would be published as a range with its basis stated, never as a point figure.

​object · required

Recorded permit work on this property, and an honest word about coverage.

Records-unavailable is never reported as an absence of permits. There is no signal available to us that distinguishes this property has no permits from this county publishes no permit records — an identifier that does not exist at all returns the same empty answer as a real property with a clean record. Three different facts, one identical response. So coverage_known is true only when at least one permit record was actually returned, and it is never inferred from an empty result.

A permit lookup that fails outright is reported the same way, and the call is still charged. The statement it produces is identical because it is identical in truth: we cannot report permit records for this property. The rest of the answer is unaffected.

Read coverage_known, not the length of permits.

​object · required

The current listing story for this property.

No agent, no office, no brokerage and no listing URL appears here. The underlying record carries a full marketing contact list — names, e-mail addresses, phone numbers and licence numbers — and none of it is published. What is published is the listing's state.

​object · required

The same facts, shaped for a CRM field or an agent chain. Every member is a boolean, a fixed category or null — no free text and no sentence.

It is computed from the chapters above it, never from a second reading of the underlying record, so it is structurally incapable of disagreeing with the detail it summarises.

A value whose chapter established nothing is null or unknown, never false. false means we checked, and no.

POST/v1/history
curl https://api.investorliftdata.com/v1/history \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <token>' \ --data '{ "address": "742 Evergreen Terrace, Springfield, IL 62701" }'
Example Request Body
{ "address": "742 Evergreen Terrace, Springfield, IL 62701" }
json
application/json
Example Responses
{ "subject_property": { "property_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90", "address": { "street": "2030 Lone Rock Dr", "city": "kingwood", "state": "TX", "zip": "77345-1234" }, "facts": { "square_feet": { "value": 2416, "source": "county-record" }, "bedrooms": { "value": 4, "source": "county-record" }, "bathrooms": { "value": 2.5, "source": "county-record" }, "partial_bathrooms": { "value": 1, "source": "county-record" }, "year_built": { "value": 1998, "source": "county-record" }, "lot_size_square_feet": { "value": 8276, "source": "county-record" }, "property_type": { "value": "Single Family", "source": "county-record" } }, "address_quality": { "code": "zip-confirmed", "statement": "The house number matched exactly one county record, and that record sits in the same ZIP code your address resolved to. Check the address and property facts in this block before relying on these numbers." }, "outside_model_range": { "flagged": false, "reasons": [] } }, "records_statement": "records_statement", "transactions": [ { "sale_date": "2024-02-14", "sale_price": 291000, "price_is_estimated": true, "kind": "standard-sale", "inter_family": true, "foreclosure": true, "reo_sale": true, "buyer_type": "individual" } ], "flips": [ { "bought_on": "2023-07-11", "sold_on": "2024-02-14", "bought_for": 205000, "sold_for": 291000, "days_held": 218, "percentage_gain": 0.4195, "seller_posture": "professional" } ], "seller_position": { "owned_since": "2019-04-11", "days_owned": 2694, "recorded_loan_available": true, "basis": "basis" }, "permits": { "permits": [ { "work_status": "completed", "job_value": 18500, "file_date": "file_date", "issue_date": "2024-03-11", "final_date": "final_date", "construction_duration": 42, "tags": { "0": "tags_roofing" } } ], "coverage_known": true, "statement": "statement", "roof": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "hvac": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "water_heater": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "electrical": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "plumbing": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "demolition": { "most_recent_date": "2024-03-11", "most_recent_job_value": 18500, "work_status": "completed" }, "has_open_permit": true }, "market_status": { "state": "active", "rental_listing": true, "days_on_market": 34, "original_listing_date": "original_listing_date", "failed_listing_date": "failed_listing_date", "sold_price": 412000, "sold_date": "sold_date", "list_price_high": 435000, "list_price_high_date": "list_price_high_date", "list_price_low": 399000, "list_price_low_date": "list_price_low_date", "price_reduced": true }, "summary": { "has_recorded_transactions": true, "last_recorded_sale_kind": "standard-sale", "last_recorded_buyer_type": "individual", "flip_detected": true, "long_tenured_owner": true, "permit_records_available": true, "open_permit_work": true, "demolition_recorded": true, "market_state": "active", "asking_price_reduced": true, "recorded_loan_available": true } }
json
application/json

Scoring