Zillow Listings API: Get Property Data as JSON
You can get Zillow listings as JSON with one GET request to /v1/zillow/search on the Perceptlayer API. Pass a latitude, longitude and radius, and the response lists properties with address, price, bedrooms, bathrooms and photos. Each successful call costs 1 credit, and a failed call costs nothing. This guide shows the request, the parameters, a trimmed real response and the cost per 1,000 calls.
Is there an official Zillow listings API?
Zillow Group runs a developer site that lists APIs for MLS listings, rentals feeds, public data and Zestimates, offered to the public and to partners. Those programs have their own terms and eligibility. If your use fits one, check that route first, because it's the authoritative one.
If you aren't, or you need a plain search-by-location call returning JSON today, that's the gap the Perceptlayer Zillow API fills. It exposes 10 endpoints under one key, with the same authentication and credit model as every site in the catalogue: Zillow is one of 248 websites and 1,659 endpoints in the catalogue.
Data is collected from publicly available sources and returned as found at the time of the call. Listing fields can change between calls, so store the timestamp of each pull.
How do you get a key and make the first call?
Sign up, verify your email and copy a key that starts with sk_live_. Send it in the X-API-Key header on every request. If you can't set headers, the api_key query parameter works instead. The free tier is 1,000 credits a month, enough for 1,000 Zillow calls, so you can run everything below without paying. The authentication docs cover both options.
Get a free API key, export it, then run this search for listings within 5 miles of a point in Seattle (latitude 47.6062, longitude -122.3321):
export API_KEY=sk_live_your_key_here
curl "https://api.perceptlayer.com/v1/zillow/search?latitude=47.6062&longitude=-122.3321&radius_mi=5&listing_category=category1&page_size=20" \
-H "X-API-Key: $API_KEY"
The response is JSON with a data object. The listings sit in data.listings, one object per property. The requests docs explain the general conventions that apply to every endpoint.
Which parameters does the search endpoint take?
/v1/zillow/search has no required parameters, but you'll always want to say where to look. Use a centre point with radius_mi, a bounding box made of four coordinates, region_ids[], or a polygon string. Everything else narrows the results by price, size, status or keyword. The table groups all 29 parameters by job.
| Group | Parameter | Type |
|---|---|---|
| Point and radius | latitude, longitude, radius_mi |
number |
| Bounding box | north_latitude, south_latitude, east_longitude, west_longitude |
number |
| Region | region_ids[] |
list of integer |
| Region | region_id_type |
integer |
| Shape | polygon |
string |
| Category | listing_category |
string: all, category1, category2 |
| Status | home_statuses[] |
list of string |
| Status | include_off_market, allow_empty |
boolean |
| Price | min_price, max_price |
integer |
| Size | min_beds, max_beds, min_baths, max_baths |
integer |
| Size | living_area_min, lot_size_min, year_built_min |
integer |
| Text | keywords |
string |
| Map | zoom_level |
integer |
| Sorting | sort_order |
string, see below |
| Sorting | sort_ascending |
boolean |
| Paging | page, page_size |
integer |
sort_order accepts one of baths, yearBuilt, price, relevance, beds, daysOn, featured, livingArea, lotArea, nearby, recentlyChanged, rentalDays or rentalPriorityScore.
The reference doesn't say what category1 and category2 select, so try each on your area and compare the results before you build on one. The example request above uses category1.
Narrowing the search
Add filters as plain query parameters. This request asks for homes with at least 3 bedrooms and 2 bathrooms between $500,000 and $900,000, sorted by price:
curl "https://api.perceptlayer.com/v1/zillow/search?latitude=47.6062&longitude=-122.3321&radius_mi=5&min_price=500000&max_price=900000&min_beds=3&min_baths=2&sort_order=price&sort_ascending=true&page_size=20" \
-H "X-API-Key: $API_KEY"
A tight filter can return few or no listings, so loosen one filter at a time if a search comes back thin. The search endpoint also takes an allow_empty boolean.
What does a search response look like?
The response wraps results in data.listings. Each listing carries a zpid (the property ID), a location, an address, a media block with photo links, and the facts a valuation or lead tool needs: bedrooms, bathrooms, livingArea, yearBuilt, propertyType, a listing status object and a price object. This is a trimmed excerpt of a recorded response to the Seattle request above:
{
"data": {
"listings": [
{
"zpid": 2068074790,
"location": {
"latitude": 47.592564,
"longitude": -122.386925
},
"address": {
"streetAddress": "1124 Sunset Avenue SW",
"zipcode": "98116",
"city": "Seattle",
"state": "WA"
},
"media": { … },
"currency": "usd",
"country": "usa",
"bestGuessTimeZone": "America/Los_Angeles",
"bathrooms": 6,
"bedrooms": 4,
"livingArea": 7870,
"yearBuilt": 2015,
"lotSizeWithUnit": {
"lotSize": 0.2652,
"lotSizeUnit": "acres"
},
"propertyType": "singleFamily",
"listing": {
"listingStatus": "forSale",
"marketingStatus": "active",
"palsId": "39001_2591845"
},
"price": {
"value": 14000000,
"pricePerSquareFoot": 1779
…
Field names are camelCase and nested. Price is at price.value, the street at address.streetAddress, and the lot size is an object with a value and a unit. The excerpt also holds media flags and recommendation blocks that we've cut with …. Treat any field you don't see in your own response as absent, and check for it in code rather than assuming it's there.
How do you fetch one property by ID?
Take the zpid from a search result and pass it to /v1/zillow/property. The endpoint has no required parameters and accepts a zpid[] list. Per-endpoint pages, such as Zillow API: Property, list the parameters and let you try the call.
curl "https://api.perceptlayer.com/v1/zillow/property?zpid[]=2068074790" \
-H "X-API-Key: $API_KEY"
We haven't recorded a property response for this guide, so we won't show one. Run the call on your own key and inspect the JSON before you map fields.
The other Zillow endpoints follow the same pattern:
| Endpoint | Use it for | Credits |
|---|---|---|
/v1/zillow/autocomplete |
Turn a typed place name into suggestions (query required) |
1 |
/v1/zillow/photos |
Photos for one or more zpid[] |
1 |
/v1/zillow/photo |
One photo by photo_id, with optional size |
1 |
/v1/zillow/building |
A building by slug, lot_id or coordinates |
1 |
/v1/zillow/schools |
Schools by ID or bounding box | 1 |
/v1/zillow/market-data |
Market statistics (family and geography required) |
1 |
How do you call it from Python?
The same request in Python needs only requests. This script searches around the same Seattle point, then prints address, bedrooms and price for each listing. It reads the key from the environment and checks the status before parsing, so a failed call raises an error instead of returning bad data.
import os
import requests
resp = requests.get(
"https://api.perceptlayer.com/v1/zillow/search",
headers={"X-API-Key": os.environ["API_KEY"]},
params={
"latitude": 47.6062,
"longitude": -122.3321,
"radius_mi": 5,
"listing_category": "category1",
"page_size": 20,
},
timeout=30,
)
resp.raise_for_status()
for item in resp.json()["data"]["listings"]:
print(
item.get("zpid"),
(item.get("address") or {}).get("streetAddress"),
item.get("bedrooms"),
(item.get("price") or {}).get("value"),
)
To page through more results, increase page and keep page_size fixed. Each page is its own call and its own credit, so pick the smallest page_size that covers your need.
What does it cost per 1,000 calls?
Every Zillow endpoint costs 1 credit per successful call, so 1,000 search calls use 1,000 credits. Failed calls cost nothing and come back with X-Credits-Used: 0. The free tier of 1,000 credits a month covers 1,000 calls. Beyond that, purchased credits last 12 months, with no subscription and no seats. The credits docs and pricing page have the detail.
| Plan | Price | Credits | Cost per 1,000 calls |
|---|---|---|---|
| Free tier | $0 | 1,000 a month | $0 |
| Starter | $10 | 12,500 | $0.80 |
| Growth | $50 | 83,000 | $0.60 |
| Scale | $200 | 400,000 | $0.50 |
A worked example: refreshing 10 search areas twice a day for 30 days is 10 × 2 × 30 = 600 calls, or 600 credits, which fits inside the free tier. A job making 10,000 calls a month uses 10,000 credits, within the Starter pack's 12,500, at $0.80 per 1,000 calls.
What happens when a call fails?
A failed call returns an HTTP status and a JSON body shaped as {"error": {"code", "message", "request_id"}}, and it isn't billed. The codes you'll most likely meet are invalid_api_key (401), insufficient_credits (402), invalid_parameters (422), rate_limited (429) and source_error (502). The errors docs list all of them.
Handle them by code, not by message text. Retry rate_limited, source_error and temporarily_unavailable (503) with a growing delay between attempts. A 429 is the standard "too many requests" status, defined in RFC 6585 (2012). Fix invalid_parameters before you retry, since repeating the same request will fail the same way. See the rate limits docs for the limits on your key.
Can an AI agent call these endpoints?
Yes. The MCP server at https://api.perceptlayer.com/mcp exposes search_endpoints, get_endpoint and call_endpoint, so an agent can find the Zillow search endpoint and call it without custom glue code. In Claude Code, add it with the command below, then ask for listings in a city. The MCP docs cover other clients.
claude mcp add --transport http perceptlayer https://api.perceptlayer.com/mcp --header "Authorization: Bearer $API_KEY"
Only successful calls are billed and failed calls cost nothing, so cap the number of calls a task may make. For more real estate sources, see the real estate APIs.
Ready to try it? Get a free API key, run the Seattle search above, and check the Zillow API page for the other nine endpoints.
APIs in this post
Frequently asked questions
Does Zillow have a public API for listings?
Zillow Group lists official APIs on its developer site, covering MLS listings, rentals feeds, public data and Zestimates, for the public and for partners. If you qualify for a program, use it. If you need a key-based search call that returns JSON now, the Perceptlayer Zillow API provides one.
How much does one Zillow search call cost?
One credit per successful call. That's $0.80 per 1,000 calls on the Starter pack ($10 for 12,500 credits), $0.60 on Growth and $0.50 on Scale. The free tier includes 1,000 credits a month after you verify an email address, with no credit card. Failed calls return X-Credits-Used: 0.
How do I search by area instead of a radius?
Use the bounding box: north_latitude, south_latitude, east_longitude and west_longitude. You can also pass region_ids[] with region_id_type, or a polygon string. All are optional parameters on /v1/zillow/search, and each search is one call and one credit.
Can I filter by price and bedrooms?
Yes. Use min_price and max_price for price, and min_beds, max_beds, min_baths and max_baths for rooms. Add living_area_min, lot_size_min or year_built_min to cut further. Combine them in one request and you still pay one credit.
Is the data complete and current?
Data is collected from publicly available sources and returned as found at the time of the call. We don't promise completeness or freshness beyond that. For anything time-sensitive, such as a price change, call again and compare against your stored copy.