Zillow Price Tax History Scraper avatar

Zillow Price Tax History Scraper

Pricing

from $15.00 / 1,000 results

Go to Apify Store
Zillow Price Tax History Scraper

Zillow Price Tax History Scraper

Property price and tax history scraper that outputs one flat row per event. Every sale, price change, and tax year becomes its own dataset record, ready to load into a spreadsheet or database without any post-processing.

Pricing

from $15.00 / 1,000 results

Rating

0.0

(0)

Developer

Kawsar

Kawsar

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Zillow Price & Tax History Scraper

Zillow Price & Tax History Scraper, Flat Row-Per-Event Property Data Extraction

Most property data tools give you one blob of JSON per address. This actor works differently. Every price change, every sale, every tax year becomes its own row in the dataset. Load the results straight into a spreadsheet or database and you can sort by date, filter by event type, and calculate year-over-year trends without touching the data first.

Who this is for

Real estate investors who want to screen deals fast. Researchers building housing market datasets. Data teams who need property transaction history in a format that works with SQL, Excel, or any BI tool without a transformation step in the middle.

What you get

Two types of rows, both in the same dataset:

Price event rows cover every transaction and listing event on record for a property — sales, listings, price reductions, pending sales. Each row has the date, dollar amount, price per square foot, percentage change from the prior event, event label, MLS source, and agent names when available.

Tax record rows cover annual property tax history. Each row has the tax year, annual tax paid, year-over-year tax change as a percentage, assessed value, and assessed value change as a percentage.

All rate fields from the source are converted to plain percentages. There are no decimal fractions to convert on your end.

Input

Three ways to look up a property. Mix and match in any combination.

ParameterTypeDefaultDescription
zpidIdsarray of stringsProperty IDs, one per line
listingUrlsarray of stringsListing page URLs, one per line
addressQueriesarray of stringsFull street addresses, one per line
timeoutSecsinteger300Total run time limit in seconds. Increase for larger batches.

At least one of the first three is required. You can mix all three in a single run.

Setting timeout for larger batches

The default 300 seconds works for small runs. For bigger lists you need to increase timeoutSecs — and set the same value in your Apify actor run settings under Timeout:

PropertiesRecommended timeoutSecs
up to 10300
up to 301000
up to 501800
up to 1003600
100+3000 per 100 properties

If the run hits the limit mid-batch, the actor stops cleanly and saves everything collected so far. It logs a warning so you can see exactly where it stopped and re-run with the remaining inputs.

How URL lookup works

Both of these URL formats are accepted without any change on your end:

https://www.zillow.com/homedetails/4315-Lago-Viento-Austin-TX-78734/58187131_zpid/
https://www.zillow.com/homedetails/4315-Lago-Viento-Austin-TX-78734

The first has the property ID in the path. The actor pulls it out directly. The second does not — the actor reads the address slug from the URL and resolves the property ID automatically before fetching history. Same output either way.

Example input

{
"zpidIds": ["58187131", "20482366"],
"listingUrls": [
"https://www.zillow.com/homedetails/4315-Lago-Viento-Austin-TX-78734/58187131_zpid/"
],
"addressQueries": ["4315 Lago Viento, Austin, TX 78734"]
}

Output

One record per property, not one record per event. All price events and tax records are spread across flat numbered columns on a single row. A batch of 50 properties produces 50 rows, each up to 90 columns wide.

Sample output record

This is what a real record looks like. Each property produces exactly one row like this, with price events in price1* through price10* columns and tax records in tax1* through tax10* columns. Columns for entries that do not exist are simply absent.

{
"inputQuery": "4315 Lago Viento, Austin, TX 78734",
"propertyId": "58187131",
"price1Date": "2026-02-11",
"price1Amount": 1299000,
"price1PerSqft": 281,
"price1Change": -1.96,
"price1Event": "Listed for sale",
"price1Source": "Unlock MLS",
"price1Buyer": null,
"price1Seller": null,
"price2Date": "2023-09-20",
"price2Amount": 685000,
"price2PerSqft": 249,
"price2Change": -7.65,
"price2Event": "Sold",
"price2Source": "ACTRIS",
"price2Buyer": "Shelly Martinez",
"price2Seller": "Shelly Martinez",
"price3Date": "2023-08-17",
"price3Amount": 741500,
"price3PerSqft": 269,
"price3Change": 0,
"price3Event": "Listed for sale",
"price3Source": "ACTRIS",
"price3Buyer": null,
"price3Seller": null,
"price4Date": "2022-04-08",
"price4Amount": 741500,
"price4PerSqft": 269,
"price4Change": 8.25,
"price4Event": "Sold",
"price4Source": "ACTRIS",
"price4Buyer": null,
"price4Seller": "Jordan Whitfield",
"tax1Year": 2025,
"tax1Amount": 14820,
"tax1TaxChange": 3.62,
"tax1AssessedValue": 612000,
"tax1ValueChange": 2.15,
"tax2Year": 2024,
"tax2Amount": 14302,
"tax2TaxChange": -1.34,
"tax2AssessedValue": 598800,
"tax2ValueChange": 8.71,
"tax3Year": 2023,
"tax3Amount": 14496,
"tax3TaxChange": 4.88,
"tax3AssessedValue": 550800,
"tax3ValueChange": -3.22,
"tax4Year": 2022,
"tax4Amount": 13821,
"tax4TaxChange": 6.14,
"tax4AssessedValue": 569200,
"tax4ValueChange": 21.43,
"fetchedAt": "2026-04-17T10:32:00Z"
}

Field naming pattern

PatternDescription
inputQueryThe exact value you passed in — ID, URL, or address
propertyIdResolved property ID
price{n}DateDate of price event n
price{n}AmountPrice in USD
price{n}PerSqftPrice per square foot
price{n}ChangePercentage change vs. the prior event. Negative = price dropped.
price{n}EventEvent label: Sold, Listed for sale, Price change, Pending sale, etc.
price{n}SourceMLS or data source name
price{n}BuyerBuyer agent name when disclosed
price{n}SellerSeller agent name when disclosed
tax{n}YearTax year
tax{n}AmountAnnual property tax paid in USD
tax{n}TaxChangeYear-over-year change in tax paid as a percentage
tax{n}AssessedValueAssessed value in USD
tax{n}ValueChangeYear-over-year change in assessed value as a percentage
fetchedAtISO 8601 timestamp of when the record was retrieved
errorOnly present when the fetch failed after all retry attempts

How the actor processes each input

  1. Property ID — goes straight to history lookup, no extra step.
  2. URL with ID in path — ID is extracted from the URL, no extra request.
  3. URL without ID — address slug is pulled from the path and used to resolve the property ID first, then history is fetched.
  4. Street address — property ID is resolved from the address first, then history is fetched.

Once the property ID is confirmed, the actor fetches up to 10 price events and 10 tax records. Everything goes into one flat record per property — price events as price1* through price10* columns, tax records as tax1* through tax10* columns. Rate fields are converted to plain percentages. Agent objects are flattened to name strings.

When something goes wrong

Every request retries up to 5 times. Wait time between retries: 1 second, 2, 4, 8. If all five fail, the actor writes a single error row for that property — with error and inputQuery filled in — and keeps going with the rest of the list. You can identify failed lookups by filtering for rows where the error field is not empty.