Zillow listing research agent skill for Claude Code and agents

This skill collects public Zillow sale, rental and recently sold listings through an Apify Actor. Choose it when an agent needs property searches, listing comparisons or property histories as structured records.

Folder on GitHub Run on Apify

The skill pairs instructions and a runner script with the Zillow For-Sale, Rent & Sold Listing Extractor Actor. It accepts location searches, Zillow URLs, property IDs or street addresses and can enrich results with property details, schools, photos and histories where available.

Install

  1. Copy the folder zillow-for-sale-rent-and-sold-listing-extractor from GitHub, or save the three files below into it.

  2. Place it in ~/.claude/skills/zillow-for-sale-rent-and-sold-listing-extractor/ for Claude Code, or ~/.agents/skills/zillow-for-sale-rent-and-sold-listing-extractor/ for Codex and other skill-aware agents.

  3. Export APIFY_TOKEN, start the agent and ask for the job in plain words; the description tells the agent when to use the skill.

  4. Or let the skills CLI place the Actor's SKILL.md for your agent: npx skills add https://recipeforagents.com --skill zillow-for-sale-rent-and-sold-listing-extractor (the SKILL.md only; the runner script comes from the GitHub folder).

SKILL.mdzillow-for-sale-rent-and-sold-listing-extractor/SKILL.md

---
name: zillow-for-sale-rent-and-sold-listing-extractor
description: "Search Zillow by city, state or ZIP for active, rental or recently sold listings, with pricing, Zestimate, agent, school, tax and price history for each property. Output fields: address, price, soldPrice, beds, baths and more, as JSON rows. Use it for requests like \"Zillow for-sale, rental and sold listings\", \"Zillow scraper\", \"real estate comps\", or a CSV/JSON export of public Zillow data."
---

# Zillow For-Sale, Rent & Sold Listing Extractor

Search Zillow by city, state or ZIP for active, rental or recently sold listings, with pricing, Zestimate, agent, school, tax and price history for each property.

- Actor: https://apify.com/nimbryxo-studio/zillow-for-sale-rent-and-sold-listing-extractor (ID `nimbryxo-studio~zillow-for-sale-rent-and-sold-listing-extractor`)
- n8n workflow for the same Actor: https://github.com/bolvein-firex/scraping-n8n-workflows/blob/main/docs/zillow-for-sale-rent-and-sold-listing-extractor.md
- Example input: `references/example-input.json`
- Input schema: `references/input-schema.json`
- Output fields: `references/output-fields.json`

Short video breakdown:
- [The Listing Was Gone Before The Portal Email](https://www.youtube.com/shorts/bNjKOMdRlSY)

## When to use

Use this skill when the user wants:

- Zillow for-sale, rental and sold listings
- Zillow scraper
- real estate comps
- a repeatable export of public Zillow data as CSV or JSON

Do not use it for private, login-protected or paywalled data.

## What it returns

One JSON row per result. Columns from the Actor's dataset schema:

- `address`: Address
- `price`: Price
- `soldPrice`: Sold Price
- `beds`: Beds
- `baths`: Baths
- `sqft`: Sqft
- `propertyType`: Type
- `status`: Status
- `daysOnZillow`: Days
- `url`: URL

All 58 declared fields: `references/output-fields.json`.

## Inputs

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `mode` | string | yes | BUY | Search active for-sale listings, rentals, or recently sold listings. |
| `search` | string | no |  | City, ZIP, neighborhood, county or state to search (for example 'Houston, TX' or '77002'). |
| `maxItems` | integer | no | 50 | Maximum number of records for the whole run across all sources (1-5000). |
| `endPage` | integer | no | 5 | Maximum search result pages per search task (1-24). Zillow returns up to 41 listings per page. |
| `startUrls` | array | no | [] | Property detail URLs, /apartments/ building URLs, or Zillow search page URLs. Search URLs keep their own filters and mode. |
| `zpids` | array | no |  | Zillow property IDs or URLs containing _zpid. Each ZPID is resolved to its canonical detail page. |
| `addresses` | array | no |  | Full street addresses to resolve to Zillow property pages, for example '1306 Castle Ct, Houston, TX 77006'. |
| `priceMin` | integer | no |  | Minimum listing price in US dollars. |
| `priceMax` | integer | no |  | Maximum listing price in US dollars. |
| `bedsMin` | string | no | any | Minimum number of bedrooms. |
| `bathsMin` | string | no | any | Minimum number of bathrooms. |
| `sqftMin` | integer | no |  | Minimum living area in square feet. |
| `sqftMax` | integer | no |  | Maximum living area in square feet. |
| `lotSizeMin` | string | no | any | Minimum lot size in square feet. |

19 more optional fields on the Actor's Input tab.

Example (`references/example-input.json`):

```json
{
  "mode": "BUY",
  "search": "Austin, TX",
  "maxItems": 25,
  "scrapeDetails": true,
  "daysOnZillow": "7",
  "sortBy": "newest"
}
```

The input passes the Actor's input schema (checked at every publish). First run: `maxItems` = 25, small enough for the 300-second sync wait; raise it once the rows look right.

## Run it

Set `APIFY_TOKEN`, then call the synchronous endpoint for small and medium runs (it waits up to 300 seconds):

```bash
curl -s -X POST "https://api.apify.com/v2/acts/nimbryxo-studio~zillow-for-sale-rent-and-sold-listing-extractor/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d @references/example-input.json > output.json
```

Or with Node 18+: `node scripts/run.mjs [input.json]`.

For large runs, start an async run and read its dataset when it finishes:

```bash
RUN=$(curl -s -X POST "https://api.apify.com/v2/acts/nimbryxo-studio~zillow-for-sale-rent-and-sold-listing-extractor/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d @references/example-input.json)
RUN_ID=$(echo "$RUN" | jq -r '.data.id'); DATASET=$(echo "$RUN" | jq -r '.data.defaultDatasetId')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?waitForFinish=60" -H "Authorization: Bearer $APIFY_TOKEN" | jq -r '.data.status'
curl -s "https://api.apify.com/v2/datasets/$DATASET/items?format=json" -H "Authorization: Bearer $APIFY_TOKEN" > output.json
```

## Cost and limits

- Apify bills the Actor's usage on your account; the Actor's Pricing tab shows the current price.
- Keep the limit in the input small for a first run.

## Guardrails

- Public data only. Do not pass cookies, sessions or private account data.
- Do not use the output for spam, harassment or unlawful discrimination.
- Check the target site's terms and local law before running at scale.
Checked against the Agent Skills format and the Actor's input schema: 15 of 15 checks pass
  • SKILL.md starts with YAML front matter
  • name is 1-64 lowercase letters, digits and single hyphens
  • name matches the skill folder name
  • description is present, at most 1024 characters and has no angle brackets
  • front matter uses only spec fields
  • compatibility is at most 500 characters
  • body has instructions
  • field descriptions end on whole sentences
  • no credential values are embedded
  • the token is read from APIFY_TOKEN
  • Example input: all 1 required input field is present
  • Example input: every input key exists in the published input schema
  • Example input: every value has the type the schema declares
  • Example input: every choice field uses one of its published options
  • Example input: numbers respect the published minimum and maximum

Runner scriptzillow-for-sale-rent-and-sold-listing-extractor/scripts/run.mjs

// Minimal runner: node scripts/run.mjs [input.json | '{"json": "input"}']
// Needs Node 18+ and APIFY_TOKEN. Without an argument it uses references/example-input.json.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const token = process.env.APIFY_TOKEN;
if (!token) {
  console.error('Set APIFY_TOKEN');
  process.exit(1);
}
const actorId = process.env.ACTOR_ID || "nimbryxo-studio~zillow-for-sale-rent-and-sold-listing-extractor";
const here = path.dirname(fileURLToPath(import.meta.url));
const arg = process.argv[2];
const raw = !arg
  ? fs.readFileSync(path.join(here, '..', 'references', 'example-input.json'), 'utf8')
  : (arg.trim().startsWith('{') ? arg : fs.readFileSync(arg, 'utf8'));
const input = JSON.parse(raw);

const res = await fetch(`https://api.apify.com/v2/acts/${actorId}/run-sync-get-dataset-items`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
  body: JSON.stringify(input)
});
if (!res.ok) {
  console.error(`HTTP ${res.status}: ${(await res.text()).slice(0, 500)}`);
  process.exit(1);
}
console.log(JSON.stringify(await res.json(), null, 2));

Example inputzillow-for-sale-rent-and-sold-listing-extractor/references/example-input.json

{
  "mode": "BUY",
  "search": "Austin, TX",
  "maxItems": 25,
  "scrapeDetails": true,
  "daysOnZillow": "7",
  "sortBy": "newest"
}

Using it

  • Keep SKILL.md, scripts/run.mjs, references/example-input.json, references/input-schema.json and references/output-fields.json together in that folder.
  • Make Node available and set APIFY_TOKEN in the agent's environment. Confirm that scripts/run.mjs references the token and the intended Actor endpoint, with no credentials embedded in the files.
  • Ask the agent to load the skill and review references/example-input.json for the requested location and mode. When ready to collect data, invoke node scripts/run.mjs from the skill folder.

Prefer MCP? The Zillow For-Sale, Rent & Sold Listing Extractor page has configs for eight clients and a shorter, API-only SKILL.md.

Questions

Can the agent look up a known property?

Use startUrls for a Zillow property URL, zpids for a property ID or addresses for a full street address. These explicit lookups receive detail enrichment even when scrapeDetails is disabled.

Can it collect rentals and sold listings?

Set mode to RENT for rentals or SOLD for recently sold listings. Check status in the returned records before combining them with active sale listings.

Does every sold listing include a sale price?

soldPrice is available when the market provides it. Keep lastSoldPrice separate because it represents the most recent recorded sale price in the property payload.

Can the agent search with property filters?

The input supports price, bedroom, bathroom, living-area, property-type and other listing filters. Validate the chosen fields against references/input-schema.json before invoking the runner.