Real Estate

How do you get Zillow listings and Zestimate-style property data via API?

Call ReefAPI's zillow search action with a location and read listings with price, beds, baths, sqft, coordinates and days on market back as JSON, then property_detail on the zpid for the Zestimate, price history and description. The two surfaces carry different fields, and neither is a superset of the other.

Zillow engineLive JSON5 steps1,000 free credits

This guide demonstrates the real Zillow API engine with a captured response from . The example is only published because the engine passed the SEO snapshot gate.

Use case

Real-estate analytics, lead generation, valuation modelling and market monitoring.

Step by step

Call the live endpoint

  1. 1

    Search by location, coordinates or URL

    search takes a place string, search_by_coordinates takes an explicit map_bounds box, and search_by_url takes a Zillow URL you already have. All three accept status ('for_sale', rentals, sold) and the usual price, beds and home_type filters.

  2. 2

    Read count, map_pins and total_reported together

    41 detailed rows, 502 pins and 5,721 reported matches came from one call. The pins are the cheap way to cover a whole city geographically.

  3. 3

    Subdivide the box rather than paginating deeper

    One map box tops out around 860 list results. fetch_all tiles the bounding box for you; meta.map_bounds tells you what box you were given so you can split it yourself.

  4. 4

    Call property_detail for valuation

    zestimate_usd was populated on 6 of 41 search rows and on the detail call. price_history, tax_history, schools and climate risk exist only there.

  5. 5

    Keep days_on_market from the search row

    It was present on all 41 search results and null on detail. If you drop the search row after enriching, you lose it.

Code

Copy the request

These snippets use the captured request params for zillow/v1/search.

curl -X POST https://api.reefapi.com/zillow/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"location":"Austin, TX"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.zillow.search with {"location":"Austin, TX"}.
Real response

Captured output from ReefAPI

Captured on UTC. The response below is the committed snapshot, including the API envelope and metadata.

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/zillow/v1/search",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "location": "Austin, TX"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "zillow",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 5517,
    "record_count": 41,
    "bytes": 1119692,
    "cache_hit": false,
    "completeness_pct": 100,
    "completeness_fields": 12,
    "completeness_basis": "search",
    "location": "Austin, TX",
    "map_bounds": {
      "west": -98.246758,
      "east": -97.346758,
      "south": 29.93245,
      "north": 30.63245
    },
    "filters_applied": null,
    "charged_credits": 2,
    "version": "1.5.0"
  },
  "data": {
    "items": [
      {
        "zpid": 29558116,
        "source": "zillow",
        "url": "https://www.zillow.com/homedetails/12600-Oro-Valley-Cv-Austin-TX-78729/29558116_zpid/",
        "list_price_usd": 549000,
        "price_display": "$549,000",
        "status": "FOR_SALE",
        "status_text": "Active",
        "property_type": "SINGLE_FAMILY",
        "beds": 4,
        "baths": 3,
        "sqft": 2472,
        "lot_sqft": 9199.872,
        "zestimate_usd": null,
        "rent_zestimate_usd": null,
        "tax_assessed_value_usd": 496142,
        "days_on_market": 6,
        "address_line": "12600 Oro Valley Cv",
        "city": "Austin",
        "state_code": "TX",
        "postal_code": "78729",
        "address": "12600 Oro Valley Cv, Austin, TX 78729",
        "latitude": 30.444155,
        "longitude": -97.765144,
        "broker_name": "Compass RE Texas, LLC",
        "is_showcase": true,
        "photos": [
          "https://photos.zillowstatic.com/fp/484019a1bc47e283f3cb3d6f79663997-p_e.jpg"
        ],
        "is_building": false,
        "identifier_type": "zpid"
      },
      {
        "zpid": 29596334,
        "source": "zillow",
        "url": "https://www.zillow.com/homedetails/12712-Twisted-Briar-Ln-Austin-TX-78729/29596334_zpid/",
        "list_price_usd": 680000,
        "price_display": "$680,000",
        "status": "FOR_SALE",
        "status_text": "Active",
        "property_type": "SINGLE_FAMILY",
        "beds": 5,
        "baths": 4,
        "sqft": 3154,
        "lot_sqft": 7871.292,
        "zestimate_usd": null,
        "rent_zestimate_usd": null,
        "tax_assessed_value_usd": 646643,
        "days_on_market": 6,
        "address_line": "12712 Twisted Briar Ln",
        "city": "Austin",
        "state_code": "TX",
        "postal_code": "78729",
        "address": "12712 Twisted Briar Ln, Austin, TX 78729",
        "latitude": 30.453896,
        "longitude": -97.78103,
        "broker_name": "Keller Williams Realty",
        "is_showcase": true,
        "photos": [
          "https://photos.zillowstatic.com/fp/2b100a3981b1fa014c2b3895b4c60abd-p_e.jpg"
        ],
        "is_building": false,
        "identifier_type": "zpid"
      },
      {
        "zpid": 185179147,
        "source": "zillow",
        "url": "https://www.zillow.com/homedetails/12500-Turkey-Ridge-Ct-Austin-TX-78729/185179147_zpid/",
        "list_price_usd": 524900,
        "price_display": "$524,900",
        "status": "FOR_SALE",
        "status_text": "Active",
        "property_type": "SINGLE_FAMILY",
        "beds": 4,
        "baths": 2,
        "sqft": 2314,
        "lot_sqft": 11835.25,
        "zestimate_usd": null,
        "rent_zestimate_usd": null,
        "tax_assessed_value_usd": 490354,
        "days_on_market": 7,
        "address_line": "12500 Turkey Ridge Ct",
        "city": "Austin",
        "state_code": "TX",
        "postal_code": "78729",
        "address": "12500 Turkey Ridge Ct, Austin, TX 78729",
        "latitude": 30.444427,
        "longitude": -97.76878,
        "broker_name": "Keller Williams Realty",
        "is_showcase": true,
        "photos": [
          "https://photos.zillowstatic.com/fp/a20103b3130fef4ef77ac2123aa4792d-p_e.jpg"
        ],
        "is_building": false,
        "identifier_type": "zpid"
      }
    ],
    "count": 41,
    "map_pins": [
      {
        "zpid": "29558116",
        "latitude": 30.444155,
        "longitude": -97.765144,
        "price_display": "$549,000"
      },
      {
        "zpid": "29596334",
        "latitude": 30.453896,
        "longitude": -97.78103,
        "price_display": "$680,000"
      },
      {
        "zpid": "185179147",
        "latitude": 30.444427,
        "longitude": -97.76878,
        "price_display": "$524,900"
      }
    ],
    "total_reported": 5723,
    "pages_fetched": 1,
    "tiles_fetched": null,
    "stop_reason": "max_pages",
    "partial": false,
    "location": "Austin, TX",
    "status": "for_sale",
    "filters_applied": null,
    "harvest_note": "SRP caps ~860 listResults/~500 map pins per box; fetch_all bbox-tiles to exceed (faceted-search-pagination)."
  }
}
Manual way

Why this is hard manually

The intuition that detail is search-plus-more is wrong on Zillow, in both directions. days_on_market was populated on all 41 of our Austin search results (60 days, 6 days, and so on) and came back null on the property_detail call. The Zestimate went the other way: populated on 6 of 41 search rows and present on detail. tax_assessed_value_usd appeared on 36 of 41 search rows and has no equivalent field on detail at all, which carries tax_history instead. Whichever call you skip, you lose something.

The second thing to plan around is that a map-based search has a ceiling that has nothing to do with how many homes match. Our 'Austin, TX' for-sale search reported total_reported 5,721 and returned 41 detailed items - because a single map box serves roughly 860 list results and about 500 map pins before it stops, and one page of that box is 41 rows. Scraping deeper is not a matter of asking for a higher page number.

The third is that 'Zestimate' and 'list price' are two different claims about the same house, and the interesting number is the gap. On 13109 Sinton Ln the list price was 539,900 against a Zestimate of 535,500, with a price_change_usd of -9,100 dated 2026-08-13. That combination - listed above the estimate, already cut once - is a story no single field tells.

ReefAPI way

Why ReefAPI solves it

Search returns three different quantities and you need all three to know what you have. count is the detailed rows you got (41), map_pins is the lightweight markers in the same box (502), and total_reported is Zillow's own count of matches (5,721). If you only need location and price for a heat map, the 502 pins are twelve times the coverage for the same call. stop_reason names why the run ended - 'max_pages' in our case - and partial tells you whether it terminated early.

The bounding box is echoed back so any search is reproducible. We passed location 'Austin, TX' and meta.map_bounds came back as west -98.246758, east -97.346758, south 29.93245, north 30.63245. Store that with your results and you can re-run the identical geography later, or hand it to search_by_coordinates to subdivide it. fetch_all does that subdivision for you when one box is not enough.

property_detail is where valuation lives: zestimate_usd, zestimate_history, rent_zestimate_usd, last_sold_price_usd and last_sold_date, price_change_usd with price_change_date, price_history, tax_history, schools, walk_transit_score, climate_risk, mls_id, parcel_id and reso_facts. Not all of them are filled on every property - our test returned 18 of 50 fields null, which is exactly why meta reports completeness_pct 80 over completeness_fields 20 with completeness_basis 'detail'. That percentage is measured against a core set, not against every field in the object.

The same completeness fields on search read 100 percent over 12 fields with basis 'search'. Two responses can both say 100 and mean very different things, so read completeness_basis and completeness_fields alongside the number rather than treating the percentage as absolute.

lot_sqft comes back with decimals (6046.128, 24668.03) because Zillow publishes lot size in acres and we convert. Rounding it to an integer is safe; assuming it was an integer to begin with is not. beds and baths are floats too, which is how a half-bath is represented.

Fourteen actions share the engine, and several of them replace whole workflows: comps returns comparable homes around a zpid with a radius you set, market_trends takes a location, home_value_chart returns a Zestimate time series over a window you choose, agent and agents_by_location cover the listing side, and new_listings takes a changed_after timestamp so a monitor can ask only for what appeared since its last run. Search answered in about 8.5 seconds, detail in 9.4 - these are heavy pages.

FAQ

Questions developers ask

Do I need a Zillow API key or partner agreement?

No. You send a ReefAPI key. There is no Zillow developer programme, partner approval or Bridge API contract involved.

Why is the Zestimate missing from most search results?

Because Zillow's map cards mostly do not carry it - it was populated on 6 of our 41 rows. property_detail returns it reliably, along with the rent Zestimate and the Zestimate history.

Why is days_on_market null on the detail call?

Because that field lives on the search card, not the property page. It was populated on all 41 of our search results and null on detail. Carry it forward from the search row rather than expecting detail to repeat it.

The search says 5,721 matches but I got 41 rows. Where are the rest?

A single map box serves roughly 860 list results and about 500 map pins, and one page is 41 rows. total_reported is Zillow's match count, not a retrievable set. Raise max_pages, use fetch_all to tile the box, or narrow the geography.

What are map_pins for?

They are lightweight markers for every property in the box - 502 of them against 41 detailed items in our run. If you need geographic coverage rather than full records, they are far more of the market per call.

completeness_pct says 100 on search and 80 on detail. Is search better data?

No - the two are measured against different field sets. Search reports 100 over 12 core fields, detail reports 80 over 20. Read completeness_basis and completeness_fields with the number; a percentage on its own is not comparable across endpoints.

Can I monitor only what changed?

Yes. new_listings takes a changed_after timestamp plus a region or postal prefix, which is much cheaper than re-searching a metro and diffing it yourself.