Zillow Property Detail Scraper avatar

Zillow Property Detail Scraper

Pricing

from $1.25 / 1,000 results

Go to Apify Store
Zillow Property Detail Scraper

Zillow Property Detail Scraper

Open one Zillow home at a time by ZPID or homedetails URL. Each row carries price, beds, baths, areaSqft, lotSizeSqft in square feet, yearBuilt, homeType, homeStatus, coordinates and the full listing description: the facts a search result card never shows. Up to 200 properties per run, no login.

Pricing

from $1.25 / 1,000 results

Rating

0.0

(0)

Developer

String

String

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 days ago

Last modified

Share

What does the Zillow Property Detail Scraper do?

This Actor reads one Zillow property at a time. You supply ZPIDs or /homedetails/ URLs, and each one comes back as a single row holding that home's own record: price, beds, baths, living area, lot size in square feet, year built, home type, listing status, exact coordinates and the full listing description. Up to 200 properties per run, one Zillow request each.

  • Every measure is a number. 675000, not "$675,000"; 9121, not "0.21 acres"
  • Lot size is normalised to square feet, so a rural acre-quoted lot compares directly with a suburban one
  • The listing's own description text arrives with HTML entities already decoded
  • A ZPID and the full slug URL for the same home collapse to one fetch, so a mixed list is never billed twice
  • No Zillow account, cookie or API key is involved at any point

This is the deep-read half of a pair. It does not search or discover anything: you tell it which homes to open. To find the ZPIDs in the first place, sweep a market with the Zillow Property Listings Scraper and feed its zpid column straight into this Actor. The two schemas overlap on price, address, beds, baths, area and coordinates; year built, lot size, home type and the description exist only here.

What data does it extract?

FieldTypeDescription
zpidstringZillow's own property ID, stable across runs, taken from the page itself
addressstringOne line: street, city, state and ZIP
citystringCity
statestringTwo-letter state code
zipcodestringZIP code
pricenumberCurrent price on the record
bedsnumberBedroom count
bathsnumberBathroom count
areaSqftnumberLiving area in square feet
lotSizeSqftnumberLot size, always in square feet
yearBuiltnumberYear of construction
homeTypestringZillow's own category, e.g. SINGLE_FAMILY, CONDO, TOWNHOUSE
homeStatusstringe.g. FOR_SALE, FOR_RENT, SOLD, PENDING
latitudenumberDecimal degrees
longitudenumberDecimal degrees
descriptionstringThe listing's own description text
sourceUrlstringThe canonical detail URL the row was read from
collectedAtstringISO timestamp of the fetch

A fact the listing omits comes back as null. Condos usually have no lot size, and new construction often has no year built yet; neither is filled in with a guess.

Why scrape Zillow property detail?

Mostly because of three fields the search results page does not carry: lot size, year built and home type. An automated valuation model or a comparable-sales spreadsheet needs all three next to living area, and no amount of sweeping search pages will produce them.

  • Enrich a portfolio or CRM you already hold, keyed on address or ZPID, with current price and status
  • Watch homeStatus on a schedule to catch the FOR_SALE to PENDING to SOLD transitions
  • Build a dated snapshot series of real property records to back-test a pricing model against
  • Follow up a market sweep by opening only the listings that looked interesting
  • Compare rural and suburban lots directly, since acre-quoted lots are converted to square feet

How to use it

  1. Hit Try for free on this page, or add the Actor to your Apify account.
  2. Paste your ZPIDs or /homedetails/ URLs into Property URLs or ZPIDs, one per line. A bare 29502073 is enough.
  3. Adjust Concurrency if you are working through a long list; the default of 5 keeps a 200-property run short.
  4. Set Maximum results if you want a hard ceiling on the dataset.
  5. Click Start, then read the rows from the Dataset tab or export them as JSON, CSV or Excel.
  6. Check the SUMMARY record in the run's key-value store for any property that failed and why.

Where do the ZPIDs come from? A Zillow URL such as https://www.zillow.com/homedetails/6310-Needham-Ln-Austin-TX-78739/29502073_zpid/ carries it in the last segment, and the listings scraper emits it as a column.

Input

FieldTypeDefaultDescription
propertiesarray of stringsrequiredBare ZPIDs (29502073), 29502073_zpid, or full /homedetails/ URLs. Between 1 and 200 entries
maxItemsinteger1000Ceiling on dataset rows, 1 to 50000
concurrencyinteger5Properties fetched in parallel, 1 to 10
{
"properties": [
"29502073",
"https://www.zillow.com/homedetails/6310-Needham-Ln-Austin-TX-78739/29502073_zpid/"
],
"maxItems": 1000,
"concurrency": 5
}

Both entries above address the same home, so that input costs one request and yields one row.

Output

One row per property. The shape below is illustrative — it shows the fields and their types, not a captured run.

{
"zpid": "29502073",
"address": "6310 Needham Ln, Austin, TX 78739",
"city": "Austin",
"state": "TX",
"zipcode": "78739",
"price": 1200000,
"beds": 5,
"baths": 4,
"areaSqft": 3496,
"lotSizeSqft": 9121,
"yearBuilt": 1989,
"homeType": "SINGLE_FAMILY",
"homeStatus": "FOR_SALE",
"latitude": 30.192585,
"longitude": -97.89119,
"description": "Pool, porch & outdoor kitchen on a quiet cul-de-sac in Circle C Ranch.",
"sourceUrl": "https://www.zillow.com/homedetails/29502073_zpid/",
"collectedAt": "2026-08-21T09:14:03.512Z"
}

Reliability

Each property is read out of the page's own data rather than from rendered text, so a value is either present and exact or null. Prices and areas never arrive as display strings needing a cleanup pass.

Zillow parks the property record inside its Next.js page data as a JSON string, keyed by whichever GraphQL query filled the client cache — one key on a showcase listing, a different one elsewhere. The Actor scans the cached responses for the property record instead of naming a key that changes with how the home is being sold, which is why a showcase listing and an ordinary one both parse.

A detail page that loads but carries no property facts (a building shell, or an interstitial served under the detail route) is recorded as a failed target rather than emitted as an all-null row. Failures land in the run's SUMMARY under failures, and a run where every property failed exits with an error.

There are no retries by design. The String Unblocker behind this Actor owns proxy rotation, session reuse and anti-bot solver selection, so a retry loop out here would re-roll the same block.

Limitations

One detail page per property, and zillow.com only, so US properties only. Photos, price history, tax history, Zestimate detail, schools, and agent or broker contact details are not collected. Discovery is out of scope: this Actor opens the homes you name and does not search by city. A Zillow URL that addresses something other than a property record, such as an apartment building under /apartments/, is reported under failures rather than fetched.

How much does it cost?

Pricing is per event: one charge for each result row written to the dataset. A property that fails produces no row and costs you nothing. The current rate is shown in the pricing panel at the top of this page.

Runs started from an Apify free plan stop at 250 requests and 250 results, and the run reports the cap in its status message. Any paid plan runs the full input and whatever maxItems you set. The cap exists because this Actor fetches through String's own infrastructure, which Apify does not reimburse on free-plan runs; it binds on requests as well as rows so a long input list cannot spend those fetches on rows the run will not return.

Using it with the Apify API

import { ApifyClient } from "apify-client";
const client = new ApifyClient({ token: "<YOUR_APIFY_TOKEN>" });
const run = await client.actor("usestring/zillow-detail").call({
properties: ["29502073", "29518186"],
concurrency: 5
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((home) => [home.zpid, home.price, home.yearBuilt]));
const summary = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord("SUMMARY");
console.log(summary?.value);

Chaining the two Zillow Actors is the common pattern: call the listings scraper first, collect the zpid column from its dataset, and pass that array straight in as properties.

The Actor reads the public property page a logged-out visitor sees. No account, no session cookie, no paywall, nothing behind a login. Reading a page that anyone can open is not something US or EU law treats as unauthorised access, and the case law of the last few years has consistently gone that way.

A property record is not personal data, and this Actor does not collect the agent or broker contact details Zillow shows elsewhere on the page. The listing description is written by the seller's agent and remains their copyright, so use it for analysis rather than republishing it verbatim. This is not legal advice; check with a lawyer if your use goes beyond internal analysis.

FAQ

Do I need a Zillow account, API key or cookies? No. The Actor reads the public detail page a logged-out visitor sees and never signs in.

What input formats does it accept? A bare ZPID (29502073), the 29502073_zpid form, or a full /homedetails/ URL whose last segment is the _zpid part. A Zillow URL that points at something other than a property record is rejected and reported under failures.

Is lot size in acres or square feet? Always square feet, in lotSizeSqft. Zillow quotes rural lots in acres; those are converted, so a 1.562-acre lot reads as 68041.

How many properties can one run collect? Up to 200, which is the ceiling on the properties input. Each property costs one Zillow request and produces one row.

Does it return photos, price history, tax history or the Zestimate? No. The row is the property fact set in the table above and nothing more.

How is this different from the Zillow Property Listings Scraper? That one searches a market and returns roughly 41 result cards per location. This one opens a specific home and returns year built, lot size, home type and the description, which no search card carries. Use them together.

What happens if one property fails? The rest of the batch still returns. The failed target and its error go into the run's SUMMARY record, and only a run where every property failed exits with an error.

Feedback

Hit a property that fails, or a field parsed wrong? Open an issue from the Issues tab on this Actor's Store page and include the ZPID that reproduces it.