APIUpdated 2026-11-02

API: Property Pulls and Enrichment

Pull filtered property lists and enrich single addresses from the property database — 1 credit per record, free records on subscriptions.

The property endpoints run on our own nationwide property database — 130M+ properties covering every county, refreshed monthly. Every record costs a flat 1 creditand includes everything: address, owner and mailing address, property details, estimated value, equity, DTV, open liens and lender, deed and sale history, distress flags, quick-list flags, and the motivation score. Subscribers get their plan's monthly credit amount as free records every billing period (a 10,000-credit plan is 10,000 free records per period), and you are only charged for records actually delivered.

Use the v2 endpoints for new work — they take the same filters object as the dashboard's Data Pulling builder. The original v1 property endpoints keep working unchanged in shape; see v1 compatibility below. Both share your API key and the error envelope, and either pairs with bulk skip tracing to go from filters to phone numbers entirely via API.

v2 at a glance

EndpointPurposeCost
POST /api/v2/property-pulls/countHow many properties match a filters objectFree
POST /api/v2/property-pullsPull up to 25,000 matching records1 credit / record after free records
GET /api/v2/property-pulls · /{id} · /{id}/resultsList pulls, check one, page through its recordsFree
GET /api/v2/property-pulls/quotaYour free-record allowance this periodFree
GET /api/v2/property-pulls/list-stacksThe List Stack slugs you can filter byFree
POST /api/v2/enrichFull record for a single address1 credit (or a free record) per match

The filters object

Every v2 pull and count takes a filters object. Only area.state is required; every other group is optional and can be omitted entirely. Ranges are {min?, max?} (min must not exceed max), tri-state flags are true / false / omitted, and dates are YYYY-MM-DD. Unknown fields anywhere inside filters (the message names the path, e.g. filters.equity.debtBurdn: unknown field), values out of range, or more entries than a list allows return 422 validation_error. Need the exact county, city or ZIP keys? GET /v2/areas (below) resolves them.

GroupFields
areastate (2-letter, required); counties as 5-digit FIPS codes (max 50); cities as upper-case names (max 20); zips 5-digit (max 100) — OR-ed together; bbox [west, south, east, north] optionally narrows to a map viewport.
listsslugs — List Stack slugs from the list-stacks endpoint (max 10), e.g. absentee, high_equity, pre_foreclosure, mortgage_maturing; match "all" (default) or "any"; params tunes a parameterised stack per slug, e.g. { "mortgage_maturing": { "window_months": 12 } } (names, ranges and defaults come from the list-stacks endpoint).
propertytypes: any of sfr, condo, townhouse, multi, mobile, land, commercial. Ranges: estValue ($), beds, baths, sqft, yearBuilt, lotAcres, units, stories, monthsSinceRenovation. At least: openPermits, stalePermitMonths. Flags: hasPool, mobileHome.
ownerFlags: occupied, absentee, corporate (true = entity-owned — LLC, trust or company; false = individual), outOfState, inherited, units24 (2–4 units). ownershipBuckets: any of <1, 1-3, 3-5, 5-10, 10-20, 20+ years. Ranges: yearsOwned, portfolioCount. At least: sold12m (properties the owner sold in the last 12 months).
equityRanges: equityPct (−100 to 100), ltv (debt-to-value %, shown as DTV in the dashboard), openLienCount, lien1Rate (%), estConfidence (0–100), debtBurdenPct (open debt plus liens and judgments as a share of value), monthsToMaturity, recentDebtAdded ($ recorded in the last 12 months). equityBuckets: any of 0, 1-20, 21-40, 41-60, 61-80, 80+. Flags: freeAndClear, lien1Adjustable, lien1Heloc, cashOutRefi. (estValue now lives under property; the old place is still accepted.)
distressfcDocTypes: foreclosure document types (max 10, the observed set). auctionWithinDays: 30, 90, 365 or "all". Range: monthsSinceLien. At least: fcEpisodes. Flag: fcReleased. Pre-foreclosure, tax delinquent, vacant and involuntary lien are list stacks (pre_foreclosure, tax_delinquent, vacant, invol_lien).
saleslastSaleDate {from?, to?}; lastSalePrice ($); appreciationPct (value vs last purchase price, %). Flags: cashPurchase, recentBuyer (bought within 2 years), longTermOwner (10+ years), nonArmsLength.
scoremotivationMin: minimum seller motivation score, 0–100.
excludepreviouslyPulled: true skips anything this account pulled in the last 30 days.
sortby: motivation_score (default), est_value, equity_pct, years_owned, last_sale_date, debt_burden_pct, appreciation_pct, months_to_maturity; dir: desc (default) or asc. Decides which records are pulled when more match than count.

POST /v2/property-pulls/count — check the match count (free)

Send a filters object and get the number of matching properties before spending anything — the same figure the dashboard shows as you build a list. total.kind is exact when the count finished, capped when more than the counting cap matched (the value is the cap), or estimate when a planner estimate was used. With exclude.previouslyPulled set, already_pulled is the overlap with your last 30 days of pulls and new_since_last_pull is what a pull would actually deliver.

Request
curl -X POST https://www.acquired-data.com/api/v2/property-pulls/count \
  -H "Authorization: Bearer ad_live_XXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "area": { "state": "PA", "counties": ["42101"] },
      "lists": { "slugs": ["absentee", "high_equity"], "match": "all" },
      "score": { "motivationMin": 60 },
      "exclude": { "previouslyPulled": true }
    }
  }'
Response
{
  "total": { "value": 8412, "kind": "exact" },
  "already_pulled": 512,
  "new_since_last_pull": 7900,
  "data_version": "2026-10"
}

POST /v2/property-pulls — create a pull

Same filters object plus count (1–25,000) and an optional list_name. The pull runs synchronously — keep the connection open; a large pull can take a minute or two. If the connection drops, the pull still finishes server-side — poll GET /v2/property-pulls/{id}. Records pulled through the API also unlock free profile views in the dashboard and show up as pulled there.

Request
curl -X POST https://www.acquired-data.com/api/v2/property-pulls \
  -H "Authorization: Bearer ad_live_XXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "area": { "state": "PA", "counties": ["42101"], "cities": [], "zips": [] },
      "lists": { "slugs": ["absentee", "high_equity"], "match": "all" },
      "property": { "types": ["sfr", "multi"], "beds": { "min": 2 } },
      "owner": { "corporate": false },
      "equity": { "estValue": { "min": 100000, "max": 600000 } },
      "score": { "motivationMin": 60 },
      "exclude": { "previouslyPulled": true },
      "sort": { "by": "motivation_score", "dir": "desc" }
    },
    "count": 500,
    "list_name": "philly-absentee-equity"
  }'
Response
HTTP 201
{
  "pull_id": "9b2c7e40-5d1a-4c3e-8f27-6b0d9e4a1c55",
  "status": "complete",
  "list_name": "philly-absentee-equity",
  "requested_count": 500,
  "matched_count": 8412,
  "pulled_count": 500,
  "free_records": 300,
  "credits_charged": 200,
  "credits_refunded": 0,
  "price_per_record": 1,
  "data_version": "2026-10",
  "source": "api",
  "filters": { "area": { "state": "PA", "counties": ["42101"], "cities": [], "zips": [] }, "...": "..." },
  "created_at": "2026-10-13T18:00:01Z",
  "completed_at": "2026-10-13T18:00:09Z",
  "error_message": null,
  "download_url": "https://www.acquired-data.com/storage/...signed-csv...",
  "results_url": "https://www.acquired-data.com/api/v2/property-pulls/9b2c7e40-5d1a-4c3e-8f27-6b0d9e4a1c55/results"
}

Here 500 records were delivered, 300 were covered by the period's free records, and 200 credits were charged. Errors: 402 insufficient_credits (balance below count minus your remaining free records), 422 validation_error (bad filters, unknown list slug, or count out of range), and 429 job_in_progress (another pull is already running on the account).

Listing pulls and fetching results

  • GET /v2/property-pulls — all pulls on the account (dashboard and API), newest first. page, per_page (default 20, max 100), optional source filter (api | app). Items have the same shape as the create response.
  • GET /v2/property-pulls/{id} — status, counts, billing fields, the filters it ran with, and once complete a fresh signed CSV download_url (1 hour) plus results_url.
  • GET /v2/property-pulls/{id}/results — rows as flat JSON with page and per_page (default 100, max 1,000). Each row is snake_case with property_id first, then keys like address, owner_1_first_name, owner_mailing_address, estimated_value, estimated_equity, equity_percent, mortgage_balance, dtv, open_lien_count, lien_1_lender, last_sale_date, preforeclosure, motivation_score, years_owned, data_version. Requesting results before completion returns 409 job_not_complete.
  • Extended records. Send "columns": "extended"on a pull (or an enrich) and the CSV and the JSON rows gain the list-stack metrics and detail columns on top of the standard set: debt burden, months to maturity, appreciation, recent debt, cash-out refi, lien 1–4 amounts and due dates, foreclosure filing / release / episodes, involuntary lien detail, transfer detail, permit counts and dates, the owner's portfolio (property count, total value and debt, average equity, newest purchase, properties sold in 12 months), quick-list flags and month-over-month changes. The exact key list is on the API reference page in your dashboard. Same price.

GET /v2/areas — find county, city and ZIP keys

filters.area wants county FIPS codes, upper-case city names and ZIPs. GET /v2/areas?q= resolves a name prefix (or a 3–5 digit ZIP prefix) to those keys, optionally narrowed with state and kind (county, city or zip) — the same search as the dashboard's area picker.

Response for ?q=mem&state=TN
{
  "areas": [
    { "kind": "city", "key": "MEMPHIS", "label": "Memphis", "state": "TN", "property_count": 298412 },
    { "kind": "zip", "key": "38109", "label": "38109", "state": "TN", "property_count": 12044 }
  ]
}

GET /v2/property-pulls/quota — your free records

Subscribers get their plan's monthly credit amount as free records each billing period. The quota endpoint tells you where you stand so you can predict what a pull will cost: remaining records are free, and anything beyond that is credit_per_record (1) each. Profile views charged in the dashboard count against the same allowance. Accounts without a subscription see subscribed: false and an allowance of 0.

Response
{
  "credit_per_record": 1,
  "subscribed": true,
  "allowance": 30000,
  "used": 2590,
  "remaining": 27410,
  "period_start": "2026-10-06T00:00:00Z",
  "period_end": "2026-11-03T00:00:00Z"
}

GET /v2/property-pulls/list-stacks

Returns the named List Stacks you can put in filters.lists.slugs — the same presets as the dashboard, each with a side of buyers (who is buying: cash_buyers, recent_buyers, investors, likely_flip, multi_property_owners) or sellers (absentee, high_equity, free_and_clear, pre_foreclosure, tax_delinquent, vacant, inherited, tired_landlord, mortgage_maturing, portfolio_selloff, and more). Parameterised stacks list their knobs under params (name, kind, default, min, max, presets) — pass a value in filters.lists.params[slug][name] to tune the stack, otherwise the default applies. clauses are the conditions a property must meet. available is false for a stack whose definition is not live in the current data drop.

Response
{
  "list_stacks": [
    { "slug": "absentee", "name": "Absentee Owners", "group": "standard", "side": "sellers",
      "description": "Owner mailing address differs from the property (vendor flag).", "available": true,
      "params": [], "clauses": [] },
    { "slug": "mortgage_maturing", "name": "Recent Mortgage Maturity", "group": "differentiated", "side": "sellers",
      "description": "Owners whose mortgage (any open lien) matures within the window.", "available": true,
      "params": [
        { "name": "window_months", "kind": "months", "label": "Maturity window", "default": 24,
          "min": 1, "max": 60, "step": 1, "unit": "mo", "presets": [6, 12, 18, 24] }
      ],
      "clauses": [ { "key": "maturing", "label": "A mortgage matures within the window" } ] }
  ]
}

From a pull to phone numbers

A completed pull can be skip-traced without downloading it: POST /v1/jobs with { "pull_id": "…", "add_ons": ["dnc"] }loads the pull's results, maps its columns automatically and runs the normal bulk job— the API version of the dashboard's "Skip trace this list".

Market Trends

Three read-only endpoints expose the Market Trends page's numbers for every county and ZIP: GET /v2/market/metrics (the metric catalogue), GET /v2/market/areas (one metric for every area of a state, or every county nationally, sortable) and GET /v2/market/areas/{kind}/{key} (one area's latest values, year-over-year changes and — on request — its time series). Each area response includes list_filters, a readyfilters object for the count and pull endpoints, so market research flows straight into a list. See the API reference in your dashboard for the fields.

Billing

  • 1 credit per record, every dataset included — there are no add-ons or dataset upcharges on property data. GET /pricing reports property_pull: 1 and a property_data block with your free-record figures.
  • Free records on subscriptions.Each billing period your plan's monthly credit amount is also your free-record allowance. A pull uses free records first; free_records in the response is how many it covered and credits_charged is pulled_count − free_records.
  • Charged only for delivered records. Credits are held for the worst case when the pull starts and the difference is refunded automatically if fewer records match than count; the response reports both credits_charged and credits_refunded. Counting is always free.
  • Enrichment is a one-record pull: 1 credit or one free record on a match, nothing on a miss.

POST /v2/enrich — single-address enrichment

Give POST /v2/enrich an address and it returns the full property record inline — the same keys as pull results — to fill gaps in your own data. street and state are required, plus either city or zip. exact_match is false when the hit came from address normalization rather than a literal match.

Request
curl -X POST https://www.acquired-data.com/api/v2/enrich \
  -H "Authorization: Bearer ad_live_XXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "address": { "street": "1234 Walnut St", "city": "Philadelphia", "state": "PA", "zip": "19107" }
  }'
Response
{
  "matched": true,
  "property_id": "42101-0541234-00",
  "exact_match": true,
  "credits_charged": 0,
  "free_record_used": true,
  "property": {
    "property_id": "42101-0541234-00",
    "address": "1234 Walnut St", "city": "Philadelphia", "state": "PA", "zip": "19107",
    "county": "Philadelphia", "apn": "054-123400", "property_type": "Single Family",
    "bedrooms": "3", "bathrooms": "2", "living_square_feet": "1650", "year_built": "1925",
    "owner_1_first_name": "Dana", "owner_1_last_name": "Reyes",
    "owner_mailing_address": "88 Ocean Ave", "owner_mailing_city": "Cape May", "owner_mailing_state": "NJ",
    "owner_type": "Individual", "owner_occupied": "false", "vacant": "false",
    "estimated_value": "412000", "estimated_equity": "298000", "equity_percent": "72.3",
    "mortgage_balance": "114000", "dtv": "27.7", "open_lien_count": "1", "lien_1_lender": "Example Bank",
    "last_sale_date": "2009-06-30", "last_sale_price": "185000",
    "preforeclosure": "false", "corporate_owned": "false", "free_and_clear": "false",
    "motivation_score": "71", "years_owned": "17", "data_version": "2026-10"
  }
}

// No match — nothing charged
{ "matched": false, "property_id": null, "credits_charged": 0, "free_record_used": false, "property": null }

Errors: 402 insufficient_credits (no free records left and balance below 1 credit) and 422 validation_error (missing street/state, or neither city nor zip).

v1 compatibility

Integrations built on POST /api/v1/property-pulls and POST /api/v1/enrich keep working with their existing request bodies — the query object with state, counties, cities, zips, quicklists, the beds/baths/sqft/year_built/value ranges and property_types; the flat address/city/state/zip enrichment body; and GET /api/v1/property-pulls, /{id} and /{id}/results. They are now served from the same database, so what changed is:

  • Price. price_per_record is always 1, and credits_charged is records delivered minus the free records the pull used. The v1 shape does not break out free_records — read the same pull from GET /v2/property-pulls/{id} or check GET /v2/property-pulls/quota if you need that figure.
  • datasets is ignored. It is still accepted, but every record already includes valuation, liens and deed history, and responses return datasets: [].
  • count goes up to 25,000 (previously 10,000).
  • Counties are resolved by name ("Philadelphia" or "Philadelphia County"; 5-digit FIPS codes also work) against the database; an unknown county returns 422 validation_error naming it.
  • Quicklists map to List Stacks. Still supported: absentee-owner, in-state-absentee-owner, out-of-state-absentee-owner, owner-occupied, high-equity, low-equity, free-and-clear, preforeclosure, notice-of-default, notice-of-sale, notice-of-lis-pendens, tax-default, involuntary-lien, vacant, vacant-lot, corporate-owned, trust-owned (treated as entity-owned), cash-buyer, tired-landlord, inherited, recently-sold, fix-and-flip. No longer supported — a request naming any of these returns 422 listing them: senior-owner, mailing-address-vacant, on-market, active-listing, expired-listing, has-hoa, listed-below-market-price.
  • Enrichment response. A hit returns { pull_id, matched: true, credits_charged (0 or 1), free_record_used, datasets: [], environment, property } where property is the full snake_case record with the same keys as pull results; a miss returns matched: false with credits_charged: 0, pull_id: null and property: null.

Everything else — pagination, download_url lifetimes, the 409 job_not_complete and 429 job_in_progress behaviour — is unchanged. When you next touch the integration, switching to v2 gets you the full filter set, free match counts and the quota endpoint.

Looking for who would buy a property rather than who owns it? See the buyer search API.

Keep reading