Documentation

Everything you need to get Zillow property data into your app, pipeline, or AI agent.

Overview

APIllow is a Zillow property data API. Send a city name, ZIP code, property ID, or Zillow URL and get back 50+ structured data fields per property — address, price, Zestimate, beds/baths, price history, tax records, school ratings, agent info, and photos.

Two ways to use it:

Authentication

All API requests require an API key passed via the X-API-Key header:

curl -H "X-API-Key: your_key" https://api.apillow.co/v1/properties

Get a free key (50 requests/month) by signing up on the homepage or through the MCP server.

Quick Start

# Search for properties in Los Angeles
curl -X POST https://api.apillow.co/v1/properties \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_key" \
  -d '{"search": "Los Angeles", "type": "sale", "max_items": 5}'

Python

import requests

resp = requests.post(
    "https://api.apillow.co/v1/properties",
    headers={"X-API-Key": "your_key"},
    json={"search": "Los Angeles", "type": "sale", "max_items": 5}
)
for r in resp.json()["results"]:
    p = r["property"]
    print(f"{p['street_address']}, {p['city']} — ${p['price']:,}")

JavaScript

const resp = await fetch("https://api.apillow.co/v1/properties", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "your_key" },
  body: JSON.stringify({ search: "Los Angeles", type: "sale", max_items: 5 })
});
const data = await resp.json();
data.results.forEach(r => console.log(r.property.street_address, r.property.price));

POST /v1/properties

Get property data from Zillow. Accepts multiple input types in a single request.

Request Body

FieldTypeDescription
searchstringFree-text search query (e.g. "Los Angeles", "Miami Beach FL")
addressesstring[]Street addresses (e.g. ["123 Main St, City, ST 12345"])
zipcodesint[]US ZIP codes (e.g. [90210, 90211])
zpidsint[]Zillow Property IDs
urlsstring[]Zillow listing URLs or search page URLs
typestringall (default), sale, rent, sold, fsbo
property_typestringFilter by property type: house, condo, townhouse, land, apartment, manufactured, multi_family
price_minintMinimum price filter (USD)
price_maxintMaximum price filter (USD)
max_itemsintMax results, 1-1000 (default 200). See pagination note below.
At least one of search, addresses, zipcodes, zpids, or urls is required. You can combine multiple in one request.

Pagination (search & ZIP code queries)

Zillow's search results pages return ~41 listings per page. For search, zipcodes, and search-page urls inputs, APIllow automatically paginates up to 5 pages (~205 results) per query to reach your requested max_items. Requests above 205 will be capped:

max_itemsPages fetchedExpected results
1-411up to max_items
42-822up to max_items
83-1233up to max_items
124-1644up to max_items
165-2055up to max_items
206+5 (capped)~205

This cap does not apply to addresses, zpids, or listing-page urls — those can go up to 1,000 per request.

To get more than 205 results from a region, split into multiple queries by ZIP code, neighborhood, or price range.

Async Flow

All requests are async for maximum speed and reliability:

  1. POST /v1/properties returns a job_id instantly (under 100ms)
  2. Poll GET /v1/results/{job_id} every 3-5 seconds
  3. When status is "complete", results are in the results array

Results are typically ready in 3-10 seconds depending on the number of properties requested.

GET /v1/results/{job_id}

Poll for results from an async batch job. The status field will be "processing", "complete", or "failed".

curl https://api.apillow.co/v1/results/YOUR_JOB_ID \
  -H "X-API-Key: your_key"

Poll every 5-10 seconds until status is "complete".

CSV export

Add ?format=csv to download the successful properties as a flat spreadsheet — one row per property, ~42 columns (address, price, beds/baths, Zestimate, agent contact, and more).

curl "https://api.apillow.co/v1/results/YOUR_JOB_ID?format=csv" \
  -H "X-API-Key: your_key" -o results.csv

CSV is only emitted once the job is "complete". While it's still processing or has failed, the normal JSON status is returned regardless of format, so your existing poll loop keeps working — just request format=csv on the final poll, or every poll and check the response Content-Type.

Nested fields that don't fit a flat cell (price_history, tax_history, nearby_schools, comps) are written as JSON strings in a single column each, so no data is lost. Feature lists are joined with "; ". The image_urls array is summarized as primary_image_url + image_count.

GET /v1/usage

Return your current monthly usage, plan, and quota. Useful for client-side budget tracking and dashboards.

curl https://api.apillow.co/v1/usage \
  -H "X-API-Key: your_key"

Sample response:

{
  "plan": "ultra",
  "monthly_limit": 10000,
  "used_this_month": 3354,
  "remaining": 6646,
  "overage_count": 0,
  "overage_rate_per_1k": 5.00,
  "billing_period_start": "2026-05-01T00:00:00+00:00",
  "billing_period_end": "2026-06-01T00:00:00+00:00"
}

Reservation-inclusive counting. used_this_month counts in-flight jobs at their submitted max_items, then settles downward as jobs complete and unused portions are refunded. This matches the number the server uses internally to enforce the monthly limit.

Period. Quota resets on the 1st of each calendar month (UTC).

Overages. Paid plans bill at overage_rate_per_1k per 1,000 requests beyond monthly_limit. Free plans and grandfathered hard-capped accounts return overage_rate_per_1k: null and never exceed the limit.

Authentication is required, but the monthly quota is not enforced on this endpoint — you can always retrieve your own usage even after exceeding the limit.

Calling /v1/usage does not itself consume quota. This endpoint reads your usage counter but never increments it, so it's safe to poll for client-side budget tracking. Standard per-plan rate limits still apply.

Input Types

Search by city

{ "search": "Austin TX", "type": "sale", "max_items": 10 }

Look up by street address

{ "addresses": ["22525 Shaker Blvd, Shaker Heights, OH 44122"] }

Multiple addresses

{ "addresses": [
    "123 Main St, Cleveland, OH 44101",
    "456 Oak Ave, Lakewood, OH 44107"
  ]
}

Search by ZIP code

{ "zipcodes": [90210, 90211], "type": "sale" }

Look up by ZPID

{ "zpids": [20794780, 20637558] }

Get data by URL

{ "urls": ["https://www.zillow.com/homedetails/123-Main-St/12345_zpid/"] }

Search page URL

{ "urls": ["https://www.zillow.com/beverly-hills-ca/"], "max_items": 20 }

Mix and match

{ "search": "Miami", "zipcodes": [33139], "type": "rent", "max_items": 50 }

Filter by property type and price

{ "zipcodes": ["80831"], "type": "sale", "property_type": "land", "price_max": 50000 }

Supported property types: house, condo, townhouse, land, apartment, manufactured, multi_family. Price filters apply at the search level, reducing unnecessary API calls.

Response Format

{
  "job_id": "4a021afd-c429-...",
  "status": "complete",
  "results": [
    {
      "url": "https://www.zillow.com/homedetails/.../20794780_zpid/",
      "success": true,
      "zpid": 20794780,
      "elapsed_seconds": 2.8,
      "property": {
        "street_address": "1735 N Fuller Ave APT 425",
        "city": "Los Angeles",
        "price": 379888,
        "zestimate": 374200,
        ... 50+ fields ...
      }
    }
  ],
  "errors": []
}

Data Fields

FieldTypeDescription
zpidintZillow Property ID
street_addressstringStreet address
city, state, zipcodestringLocation
latitude, longitudefloatGeocoordinates
priceintListing price (USD)
last_sold_priceintLast recorded sale price
zestimateintZillow's estimated value. Null for active listings (FOR_SALE, FOR_RENT, PENDING, CONTINGENT) — only populated for sold/off-market homes.
rent_zestimateintEstimated monthly rent
bedrooms, bathroomsint/floatBed and bath count
living_areaintInterior sqft
lot_sizefloatLot size (sqft)
year_builtintYear constructed
property_typestringSINGLE_FAMILY, CONDO, TOWNHOUSE, etc.
home_statusstringFOR_SALE, SOLD, FOR_RENT, OTHER
descriptionstringFull listing description
hoa_feefloatMonthly HOA fee
days_on_zillowintDays listed
page_view_countintZillow page views
favorite_countintTimes favorited
price_historyarrayPrice events with date, event, price, source
tax_historyarrayTax records with year, tax paid, assessed value
listing_agentobjectAgent name, phone, email, company
listing_brokerstringBrokerage name
nearby_schoolsarraySchools with rating, grades, distance
image_urlsarrayProperty photo URLs

Error Handling

HTTP CodeMeaning
200Success (check results and errors arrays)
400No valid input provided
401Invalid or missing API key
429Rate limit or monthly quota exceeded

Individual property failures appear in the errors array with details:

{
  "url": "https://www.zillow.com/homedetails/invalid/0_zpid/",
  "success": false,
  "error": "HTTP 404",
  "attempts": 3
}

MCP Server Installation

The MCP server lets AI agents (Claude, Cursor, Windsurf, etc.) access Zillow property data directly as tools.

Install

pip install apillow-mcp

Claude Desktop

Add to ~/.claude/claude_desktop_config.json:

{
  "mcpServers": {
    "apillow": {
      "command": "apillow-mcp",
      "env": {
        "APILLOW_API_KEY": "your_key"
      }
    }
  }
}

Claude Code

claude mcp add apillow -- apillow-mcp
No API key yet? The agent can create one for you — just ask it to sign up.

MCP Tools

Property Data

ToolDescription
search_properties(query, type, max_items)Search by city or area name
search_by_zip(zipcodes, type, max_items)Search by ZIP codes
get_property(zpid)Look up by Zillow Property ID
get_property_by_url(url)Get data from a Zillow listing URL
get_property_by_address(address)Look up by street address
get_properties_by_addresses(addresses)Bulk address lookup

Account Management

ToolDescription
signup(email)Create free account, get API key instantly
check_usage()See current month's usage, quota, and plan
upgrade_plan(plan, email)Get a Stripe checkout URL to upgrade

Zero-Browser Signup

Users can sign up and start querying entirely through the AI agent:

# User asks their AI agent:
"Sign me up for Apillow with user@example.com"

# Agent calls signup("user@example.com") and gets:
{
  "api_key": "zs_abc123...",
  "plan": "free",
  "monthly_limit": 50
}

# Agent can immediately query:
"Find 3-bedroom homes under $500K in Austin TX"

# When they hit the limit, agent suggests upgrading:
"You've used 50/50 requests. Upgrade to Pro?"
# Agent calls upgrade_plan("pro") → returns Stripe URL

The only step requiring a browser is the Stripe payment page (PCI compliance).

Plans

PlanPriceRequests/moRate Limit
Free$0505/min
Pro$9.99/mo3,33320/min
Ultra$29.99/mo10,00060/min
Mega$99.99/mo50,000120/min

Billing API

POST /billing/signup

Create a free API key. No authentication required.

{ "email": "user@example.com" }

GET /billing/usage?api_key=your_key

Check current usage and quota.

POST /billing/checkout

Generate a Stripe Checkout URL for upgrading to a paid plan.

{ "email": "user@example.com", "plan": "pro" }

Returns a checkout_url — open in browser to complete payment.

POST /billing/webhook

Stripe webhook endpoint for subscription events. Configured in your Stripe Dashboard.