# Zillow Leads & Property Data: Agent, Price & Tax History (`germane_binoculars/zillow-leads-property-data`) Actor

Zillow listings enriched with agent/broker contact info, full price-history timeline, 20yr+ tax history, foreclosure/distress flags, schools, and resoFacts (heating/cooling/basement/construction) at scale. Pay-per-event pricing, no tiers.

- **URL**: https://apify.com/germane\_binoculars/zillow-leads-property-data.md
- **Developed by:** [matata\_cK](https://apify.com/germane_binoculars) (community)
- **Categories:** Real estate, Lead generation, AI
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 listing-rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Zillow Leads & Property Data

Zillow listings enriched with agent/broker contact info, full price-history
timeline, 20yr+ tax history, foreclosure/distress flags, schools, and
resoFacts (heating/cooling/basement/construction) at scale. No other Zillow
actor extracts these at this depth.

### TL;DR

- **Bare listings arrive fast** (address, price, beds/baths, status): this
  data comes straight off Zillow's search results.
- **Enriched rows (agent contact, price/tax history, foreclosure, schools,
  resoFacts) take longer.** See "Why enrichment is slower than listings"
  below before assuming something's stuck.
- **Dedup is built in**: upload `dedupZpids`/`dedupMlsIds` from your last
  order and we never re-charge you for rows you already have.
- **No tiers, pay-per-event**: `actor-start` once, then per row at the
  depth you actually received.
- Minimums always hold (1000 listings / 500 enriched by default, applies to
  every mode) even if that means a short wait for the shortfall to collect.

### Overview

This actor sources live Zillow listing data and, at `enriched` depth, joins
in the fields that make a listing actually useful to lead-gen, investors,
appraisers, and proptech: agent/broker contact, full price and tax history,
foreclosure/distress signals, schools, and the resoFacts long tail
(heating/cooling/basement/construction) that most competitors don't surface
at scale.

Every row carries `detail_url`, the live Zillow listing link, so you can
always verify a row against the source. Rows never carry raw scraped
HTML/JSON, only the extracted field set below.

### What you get

| Segment | Fields |
|---|---|
| Lead-gen, agents, mortgage, insurance, wholesalers | Agent/broker name, phone, email, license, brokerage, MLS attribution |
| Wholesalers, flippers, hedge funds, hard-money | Foreclosure/distress type, dates, loan/past-due amounts |
| Appraisers, iBuyers, AVMs, analysts | Full price-history timeline (event/date/price/source/$-per-sqft) |
| Tax analytics, holding-cost investors, insurance | 20yr+ tax history + assessed value |
| Relocation, family search | Assigned + nearby schools |
| Title, county aggregators, proptech | Parcel number/ID, subdivision, zoning |

### Choosing a mode (start here)

Every order needs exactly one `mode`. It's the first field in the input
form, and it determines which OTHER fields actually do anything. The Apify
Console form shows all three modes' fields at once (Apify doesn't support
hiding fields conditionally), so use this table to know which section to
actually fill in:

| Mode | Required fields | Optional fields (this mode) | Fields this mode ignores |
|---|---|---|---|
| `catalog` | none | `metro` (scopes the snapshot) | `bounds`, `depth`, `source` |
| `custom_search` | `bounds` OR `metro` | `depth`, `minListings`, `minEnriched` | `source` |
| `recent_activity` | none (`source` defaults to `agent`) | `source`, `minEnriched` | `bounds`, `metro`, `depth`, `minListings` (see note) |

Fields NOT in this table (`dedupZpids`/`dedupMlsIds`, `timeoutSecs`) apply
the same way regardless of mode.

Whichever mode you pick, check your run's key-value store for an
`ORDER_SUMMARY` record once the run starts: the raw **Input** tab always
shows every field above regardless of mode (Apify's Console form submits
the whole schema unconditionally; it can't hide irrelevant fields), so a
`catalog` run's Input tab still shows a populated `bounds` even though
catalog ignored it entirely. `ORDER_SUMMARY` is the unambiguous answer:
only what actually applied, plus, once rows start arriving,
`cities_included`, the real city/suburb names your bounds or metro pick
actually returned (see "Custom search" below for why that's often more
than one city).

`recent_activity` technically accepts `minListings` without erroring, but
the sitemap feed is always fully enriched and doesn't distinguish "listings"
from "enriched" the way `custom_search` does; use `minEnriched` there.

- **Catalog**: instant snapshot from our pre-seeded showcase metros.
  Fastest option; returns in seconds since it never needs to wait on live
  collection at all (pulled straight from our own cache). No fields
  required, just set `mode: catalog`. As of this writing the catalog pool
  covers **New York City, Los Angeles, Chicago, Houston, and Phoenix**
  (~1000 listings each, more metros added over time). `ORDER_SUMMARY`
  states the current coverage on every catalog run so you don't have to
  track it here. Optionally set `metro` to scope the snapshot to just one
  of those metros instead of the whole pool (still instant: this only
  filters what's already cached, it never triggers live collection).
- **Custom search**: supply either `metro` (pick a named metro from the
  list, the easy option, no lat/lng math) or `bounds`, a lat/lng bounding
  box for a precise custom area `metro` doesn't cover: `north`/`south` are
  latitude, `east`/`west` are longitude, and `north` must always be the
  LARGER (more northerly) value, with `south`/`west` the smaller ones. A
  swapped box (e.g. `north` given as the smaller number) is rejected
  immediately with an explanatory error rather than silently returning
  nothing, and so is an order with neither `metro` nor `bounds` set. If you
  set both, `bounds` wins. Either way this catches a whole metro area
  (adjacent suburbs included), not just one named city: e.g. a Phoenix
  query genuinely also returns Scottsdale, Tempe, Glendale, and other real
  suburbs, confirmed against our own collected data; check
  `ORDER_SUMMARY`'s `cities_included` on your run to see exactly which
  ones landed in yours. The only mode where `depth: listings` (see "Bare
  vs enriched schema" below) is available as a cheaper/faster alternative
  to full enrichment.
- **Recent activity**: Zillow's sitemap feed of newest/changed listings
  nationwide, no bounds needed. `source`: `agent` (bulk of inventory) or
  `owner` (FSBO, smaller feed). Always fully enriched: the feed itself
  carries no fields beyond a listing URL, so there's no cheaper "bare"
  variant to offer here.

Custom search and recent-activity orders that aren't already covered by our
existing coverage are collected live; this can take a while (see
"Why enrichment is slower than listings" and `timeoutSecs`), and you'll get
a partial dataset if the timeout elapses first.

### Caps, explained

Two numbers control how much you get; both are **floors, not targets**:
you may receive more than the minimum in one delivery (e.g. Apify's KV
transport ships in ~100-row chunks, and you'll get the whole chunk that
crosses the line), but never less unless `timeoutSecs` runs out first.

- **`minListings`** (default and recommended value: 1000, matching our
  1000-per-metro catalog seeding target): the floor for total rows, at
  whatever depth you asked for. Applies to every mode. It can technically be
  set as low as 1 (there's no hard-coded floor beyond rejecting
  zero/negative input); do that only for a quick test run (e.g. 10-50)
  before committing to a real order. 1000 is what we'd suggest for anything
  you actually intend to use.
- **`minEnriched`** (default and recommended value: 500): the floor for how
  many of those rows must be fully enriched. Only meaningful when
  enrichment is actually happening: `catalog`, `recent_activity`, or
  `custom_search` with `depth: enriched`. For `custom_search` with
  `depth: listings`, this is forced to `0` internally regardless of what you
  pass, since a listings-depth order never enriches anything by design, so
  there's nothing for this number to floor. If you set both by hand,
  `minEnriched` must be <= `minListings`: asking for more enriched rows
  than total rows is a nonsensical order.
- See the example inputs below for both a quick low-cap test and a full
  1000/500 order.
- `timeoutSecs` (default 7200 = 2 hours) is the most wall-clock time we'll
  spend trying to reach these floors before handing back a partial dataset.
  A fully cache-satisfiable order returns in seconds regardless of this
  value.

### Bare vs enriched schema: know what fields to expect

**`depth: listings` rows only ever include NON-NULL fields from this
whitelist**: don't assume every key below is present on every row; a field
Zillow simply didn't return for that listing is omitted entirely rather than
sent as `null`. This is the ONLY thing a `depth: listings` row will ever
contain: no agent contact, no history, no resoFacts, even if we happen to
already have that listing fully enriched from an earlier order (we
deliberately strip it back down so you never pay the listings rate for
enriched-rate data):

| Group | Fields |
|---|---|
| Identity / link | `zpid`, `mls_id`, `detail_url` |
| Price / status | `price`, `status_type`, `status_text` |
| Size / type | `bedrooms`, `bathrooms`, `living_area`, `home_type`, `lot_area_value`, `lot_area_unit` |
| Location | `address_street`, `address_city`, `address_state`, `address_zipcode`, `latitude`, `longitude` |
| Timing | `year_built`, `days_on_zillow` |
| Open house | `has_open_house`, `open_house_start`, `open_house_end` |
| Occupancy/ownership flags | `is_non_owner_occupied`, `is_zillow_owned`, `country` |
| Listing-type flags | `is_fsba`, `is_fsbo`, `is_bank_owned`, `is_coming_soon`, `is_for_auction`, `is_foreclosure`, `is_new_home`, `is_open_house`, `is_pending` |

**`depth: enriched` rows (the default for every mode) include everything
above, PLUS** the full groups from "What you get" below: agent/broker
contact, foreclosure/distress detail, complete price-history and
20yr+ tax-history timelines, assigned + nearby schools, resoFacts
(heating/cooling/construction/parking/etc.), HOA fees, mortgage/market
timing, and the structural/legal/rural/rental long tail. Enriched rows can
still omit individual fields Zillow didn't return for that listing (e.g. a
condo with no heating/cooling data), but the row is never retroactively
stripped back down the way a listings-depth row is.

### Why enrichment is slower than listings

We deliberately don't drive a full browser automation stack (no
Playwright/Puppeteer/Selenium session juggling); collection uses a lighter,
more targeted request method instead. That keeps bare listing data flowing
quickly. But Zillow runs an aggressive Web Application Firewall in front of
its detail pages, and getting flagged there risks losing collection
entirely, not just slowing it down for one order. So enrichment (agent
contact, price/tax history, foreclosure, schools, resoFacts) is paced
deliberately conservatively: correctness and continued access matter more
than raw speed. In practice: expect bare listings within seconds to minutes,
and budget real wall-clock time (`timeoutSecs`) for enrichment on anything
not already in our existing coverage.

### Example inputs

Each block below is a complete, ready-to-run input; paste it directly into
the **Input JSON** tab (switch from the regular form editor via the toggle
above the input form) to skip filling in the form by hand.

**Quick test run (small, cheap, fast, before committing to a large order):**

```json
{
  "mode": "custom_search",
  "bounds": { "north": 40.75, "east": -73.7, "south": 40.7, "west": -73.78 },
  "depth": "listings",
  "minListings": 10,
  "timeoutSecs": 300
}
```

**Catalog (instant snapshot, no fields to set):**

```json
{ "mode": "catalog" }
```

**Catalog, scoped to one metro (still instant, filters the cache, no live collection):**

```json
{ "mode": "catalog", "metro": "Chicago" }
```

**Custom search by metro name (no lat/lng math needed):**

```json
{
  "mode": "custom_search",
  "metro": "Phoenix",
  "depth": "enriched",
  "minListings": 1000,
  "minEnriched": 500
}
```

**Custom search, bare listings only (cheaper, faster):**

```json
{
  "mode": "custom_search",
  "bounds": { "north": 40.75, "east": -73.7, "south": 40.7, "west": -73.78 },
  "depth": "listings",
  "minListings": 1000
}
```

**Custom search, fully enriched:**

```json
{
  "mode": "custom_search",
  "bounds": { "north": 34.34, "east": -118.12, "south": 33.7, "west": -118.67 },
  "depth": "enriched",
  "minListings": 1000,
  "minEnriched": 500
}
```

**Recent activity, newest/changed agent-listed inventory:**

```json
{ "mode": "recent_activity", "source": "agent" }
```

**Recent activity, FSBO (owner-listed) only:**

```json
{ "mode": "recent_activity", "source": "owner" }
```

**Repeat order with dedup** (never re-ship what you already have; reuse the
`DEDUP_UPDATE` key from your prior run's key-value store):

```json
{
  "mode": "custom_search",
  "bounds": { "north": 40.75, "east": -73.7, "south": 40.7, "west": -73.78 },
  "dedupZpids": [43814015, 43828670],
  "dedupMlsIds": ["A12046823"]
}
```

### Zero rows with no obvious reason?

A run can finish `SUCCEEDED` with zero rows for a legitimate reason (bad
input, mostly) rather than a real failure; we don't fail the run outright
for this, since the collection service itself worked fine. Check your run's
**Key-value store** tab (not the Dataset tab): a rejected order (e.g. a
malformed `bounds` box) writes a `STATUS` key there,
`{"message": "<reason>"}`, so you don't have to go hunting through logs or
run metadata to find out why. (We also set it as the run's status message,
but don't rely on that alone being visible once a run has finished; the
`STATUS` key is the reliable place to look. An agent can fetch it directly
with `get-key-value-store-record`.)

### Dedup

Pass `dedupZpids`/`dedupMlsIds` with IDs you've already received from a
prior run and we'll never re-ship them. Every delivery that returns at
least one row writes a `DEDUP_UPDATE` key to that run's key-value store
(`{"zpids": [...], "mls_ids": [...]}`), the union of what you told us you
already had plus everything this run just shipped; copy those two arrays
into `dedupZpids`/`dedupMlsIds` on your next order to keep your delivered
set current. It's in the key-value store rather than the dataset
deliberately, so it never shows up as a stray non-listing row if you're
iterating dataset items programmatically.

### Pricing (pay-per-event, no tiers)

| Event | When | Price |
|---|---|---|
| `actor-start` | once per run | $0.00001 |
| `listing-row` | per row at `listings` depth | $0.0007 (~$0.70 per 1000 rows) |
| `enriched-row` | per row at `enriched` depth | $0.0012 (~$1.20 per 1000 rows) |

### Operational note

If collection is briefly interrupted, your order queues and resumes
automatically, no action needed on your end. If it's taking longer than
expected, that's the WAF-safe pacing described above at work, not a stall.

### Use from an AI agent (MCP)

This Actor is callable as a tool by AI agents via Apify's
[actors-mcp-server](https://github.com/apify/apify-mcp-server). Once the
server is wired into an MCP-capable client (Claude Desktop, Cursor, ZCode,
OpenCode, etc.), the agent sees this Actor as a tool whose input schema is
the form above and can invoke it mid-task, no manual input-form filling.

Two connection modes:

- **Local stdio** (`npx @apify/actors-mcp-server`): exposes any Actor your
  Apify API token can run, **including this one before it's Store-published**.
  Filter the server to just this Actor with
  `--tools germane_binoculars/zillow-leads-property-data`. This is how to try
  it against your own account today.
- **Hosted remote** (`https://mcp.apify.com`): discovers Actors from the
  Apify Store, so this mode applies once the Actor is published. Per Apify's
  own docs, this path also supports output schema inference (field-level
  types) that the local stdio path doesn't. Either way, this Actor ships its
  own explicit dataset schema (`.actor/output_schema.json`, 229 fields), so
  an agent can know the exact result shape up front regardless of which
  connection mode it uses.

The input fields above are written to be self-documenting for an agent:
each carries a plain-language description of when it applies and how the
value is interpreted, so an LLM can construct a valid call without reading
this README first. `catalog` mode (the default) is the fastest agent smoke
test: it returns cached rows in seconds with no bounding box or live
collection, and no polling required, so an agent can confirm the
integration works end-to-end in one round-trip.

#### Long-running orders: fire, poll, fetch

A live-collect order (`custom_search` or `recent_activity`) takes real
wall-clock time (minutes, not seconds) because enrichment (agent contact,
price/tax history) is paced for WAF safety. A single MCP tool call won't
block long enough to see it through, and that's by design. The natural agent
interaction is three steps:

1. **Fire**: call the Actor tool with your input + a generous `timeoutSecs`.
   Sustained enrichment throughput is rate-limited for site-safety reasons,
   so a default-sized order (`minListings: 1000`, enriched) realistically
   takes **90-190 real minutes**, not the 600s a smaller test order might
   suggest. 600s+ is only "generous" for a small test run (`minListings:
   10-50`); for anything near the 1000-row default, budget close to the
   full 7200s default rather than overriding it lower. The tool blocks up
   to 45s (its own `waitSecs` cap, separate from `timeoutSecs`), then if
   the run is still going it returns a status line like *"RUNNING for 40s.
   2 results so far"* plus a `nextStep` telling you exactly how to poll.
   Don't treat this as an error; the run is healthy and collecting.

2. **Poll**: follow `nextStep` by calling `get-actor-run` with the `runId`
   and `waitSecs: 30` repeatedly until the status reads `SUCCEEDED` (or
   `TIMED-OUT` with partial results). Each poll shows incremental progress
   (the result count climbs), so you can watch collection advancing.

3. **Fetch**: once terminal, call `get-dataset-items` with the `datasetId`
   from the response to retrieve the actual rows.

`timeoutSecs` (your input) governs how long collection is allowed to run
before the run returns a partial dataset; set it to comfortably exceed the
worst case for your order size. It is NOT the same as `waitSecs` (the MCP
tool's single-call block cap of 45s): the two are independent, and a
correct agent flow respects both.

# Actor input Schema

## `mode` (type: `string`):

Pick ONE mode; only that mode's fields apply. Catalog: instant snapshot from the pre-seeded catalog pool, returns in seconds, always fully enriched; metro (below) optionally scopes it to one metro, otherwise the whole pool counts. Custom search: your own lat/lng bounding box or a named metro, live-collected, the only mode with a cheaper 'listings' depth option. Recent activity: Zillow's sitemap feed of newest/changed listings nationwide, no bounds, always fully enriched.

## `metro` (type: `string`):

Pick a named metro instead of typing lat/lng bounds by hand. Covers the metro's whole urbanized area, not just city limits, so results genuinely include real suburbs along with the named city (e.g. Phoenix also returns Scottsdale/Tempe/Glendale) — see README. For custom\_search: required if bounds is not set; bounds always wins when both are present. For catalog: optional — scopes the instant snapshot to this metro instead of the whole cached pool; leave unset to use everything cached. For recent\_activity: ignored.

## `bounds` (type: `object`):

custom\_search only (ignored for catalog/recent\_activity). A lat/lng box, for a precise custom area metro doesn't cover. North/south are latitude, east/west are longitude, and north/east must each be numerically LARGER than south/west respectively (it's a box, not two arbitrary points; a swapped box is rejected). Takes priority over metro if both are set. Leave empty to use metro instead.

## `depth` (type: `string`):

Only read when mode = custom\_search; ignored (always enriched) for catalog and recent\_activity. 'listings': bare address/price/beds/baths/status, cheaper, faster (see README 'Bare listing schema'). 'enriched': full agent contact, price/tax history, foreclosure, schools, resoFacts (see README 'Enriched schema'), costs more per row.

## `source` (type: `string`):

Which Zillow sitemap feed to pull recent activity from. Always fully enriched (no bare/listings option exists for this mode; the sitemap feed itself carries nothing beyond a listing URL, so there's no cheaper variant to offer).

## `minListings` (type: `integer`):

Optional, defaults to 1000 (the recommended floor). The floor for how many rows (bare or enriched, whichever depth you asked for) this run must return before considering itself done. Applies to every mode. Set lower (e.g. 10-50) only for a quick test run before committing to a full one.

## `minEnriched` (type: `integer`):

Optional, defaults to 500 (the recommended floor). Only meaningful when rows are actually being enriched: custom\_search + depth=enriched, catalog, or recent\_activity. Ignored (forced to 0 internally) for custom\_search + depth=listings, since a listings-depth order never enriches anything by design. Must be <= whatever minListings you set; asking for more enriched rows than total rows is a nonsensical order and won't be satisfiable.

## `dedupZpids` (type: `array`):

Optional. zpids you've already received from a prior run, e.g. \[43814015, 43828670]; never re-shipped.

## `dedupMlsIds` (type: `array`):

Optional. MLS IDs you've already received from a prior run, e.g. \["A12046823"]; covers re-listings, which get a new zpid but keep the same MLS ID, so zpid-only dedup would miss them.

## `timeoutSecs` (type: `integer`):

Optional, defaults to 7200 (2 hours), minimum 300 (5 minutes). Max wait for the minimums to be satisfied before returning a partial dataset. A cache-satisfiable order (catalog, or already-covered ground) finishes in seconds regardless; this only matters when live collection is needed. The 5-min floor exists because live collection needs that long for PX-safe warmup pacing.

## Actor input object example

```json
{
  "mode": "catalog",
  "depth": "enriched",
  "source": "agent",
  "minListings": 1000,
  "minEnriched": 500,
  "dedupZpids": [],
  "dedupMlsIds": [],
  "timeoutSecs": 7200
}
```

# Actor output Schema

## `results` (type: `string`):

Every listing row returned by this run, as raw JSON.

## `runInfo` (type: `string`):

This run's key-value store: DEDUP\_UPDATE (re-upload on your next order) and, on a zero-row run, STATUS explaining why.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "dedupZpids": [],
    "dedupMlsIds": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("germane_binoculars/zillow-leads-property-data").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "dedupZpids": [],
    "dedupMlsIds": [],
}

# Run the Actor and wait for it to finish
run = client.actor("germane_binoculars/zillow-leads-property-data").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "dedupZpids": [],
  "dedupMlsIds": []
}' |
apify call germane_binoculars/zillow-leads-property-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,germane_binoculars/zillow-leads-property-data"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/yNTgnCGZiP8es5fwZ/builds/4UicUOxjkGen5AZrq/openapi.json
