# Propwire Scraper (`automation-lab/propwire-property-leads-scraper`) Actor

Export filtered public PropWire property leads with addresses, values, equity, property facts, sale data, and motivated-seller flags for acquisition prospecting.

- **URL**: https://apify.com/automation-lab/propwire-property-leads-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 71.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Propwire Scraper

Build repeatable real-estate acquisition prospect lists from public PropWire searches.

Propwire Scraper accepts the filtered URL you create on PropWire and exports one structured row per property.
Each row includes its address, property facts, estimated value and equity, sale data, and active motivated-seller or ownership flags.
It is designed for investors, wholesalers, acquisition teams, analysts, and automation builders who need a reusable PropWire data export instead of manual copying.

The Actor works with public search results and does not require a PropWire login.
It preserves the filters encoded in your URL, paginates the matching results, and deduplicates properties by PropWire ID.

### What can you do with PropWire property leads?

- Build city- or state-specific acquisition lists.
- Export absentee-owner, high-equity, preforeclosure, and other supported lead segments.
- Refresh the same filtered list on a schedule.
- Compare dataset snapshots to identify newly visible properties.
- Send structured prospects to a spreadsheet, CRM staging table, warehouse, or scoring model.
- Combine estimated value, equity, property type, and sale history in a screening workflow.
- Replace a manual PropWire search-and-copy step with an API-callable Actor.

### Who is this Propwire scraper for?

**Real-estate investors** can export targeted public property searches for underwriting and outreach preparation.

**Wholesalers and acquisition teams** can create consistent lead batches around geography, equity, ownership, or distress criteria.

**Analysts** can aggregate property types, estimated values, equity bands, and lead flags from a defined market search.

**Automation builders** can run the same input through the Apify API, schedules, webhooks, Make, Zapier, n8n, or MCP.

**Data teams** can ingest stable typed fields rather than parsing a visual app page.

### How the extraction works

1. You build a search on `propwire.com`.
2. PropWire stores the selected criteria in the URL's `filters` parameter.
3. The Actor opens that search in a coherent browser and proxy session.
4. It captures PropWire's public search response.
5. It requests additional result pages in the same session until `maxItems` is reached or results end.
6. It normalizes the records and deduplicates them by PropWire property ID.
7. It saves accepted properties to the default Apify dataset.

The browser is required because PropWire protects page and API access with session and browser checks.
Images, fonts, and media are blocked to reduce transfer without removing property data.

### Data extracted

| Field | Description |
| --- | --- |
| `propertyId` | Stable PropWire property identifier |
| `sourceUrl` | Filtered search URL that produced the row |
| `propertyUrl` | PropWire property URL |
| `fullAddress` | Combined public property address |
| `address` | Street, city, state, and ZIP components |
| `propertyType` | PropWire property type code |
| `bedrooms` / `bathrooms` | Public room counts when available |
| `yearBuilt` | Construction year when available |
| `buildingAreaSqFt` | Building area in square feet |
| `livingAreaSqFt` | Living area in square feet |
| `lotSizeSqFt` | Lot area in square feet |
| `estimatedValue` | PropWire estimated value in USD |
| `estimatedEquity` | PropWire estimated equity in USD |
| `estimatedEquityPercent` | PropWire estimated equity percentage |
| `lastSoldPrice` | Most recent recorded sale price |
| `lastSoldDate` | Most recent recorded sale date |
| `ownerName` | Owner name when included in the public search response |
| `activeLeadTypes` | Active lead and ownership flags |
| `scrapedAt` | UTC collection timestamp |

Fields can be `null` when PropWire does not provide the value in its public search response.
The Actor does not invent or infer missing values.

### Supported PropWire filters

The Actor preserves filters that PropWire itself places in a valid search URL.
Examples include:

- city and state locations;
- multiple states;
- property types;
- lead types;
- estimated value ranges;
- estimated equity percentages;
- other criteria supported by the current PropWire search interface.

Create complex filters in PropWire rather than hand-editing encoded JSON.
This keeps your input aligned with the source's current filter vocabulary.

### Getting started

1. Open PropWire in your browser.
2. Go to property search.
3. Select a real location and any desired property, equity, value, or lead filters.
4. Copy the complete URL beginning with `https://propwire.com/search?filters=`.
5. Paste it into **PropWire search URLs**.
6. Set **Maximum properties**.
7. Leave Apify Proxy enabled unless you have a tested custom proxy.
8. Start the Actor.
9. Open the **Property leads** dataset view.
10. Download JSON, CSV, Excel, XML, or RSS, or consume the dataset through the API.

The prefilled Chicago Heights input is a working search and is suitable for a quick first run.

### Input parameters

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `startUrls` | array | Yes | Working example | Complete PropWire `/search?filters=...` URLs |
| `maxItems` | integer | No | `100` | Maximum unique properties across all URLs; 1–10,000 |
| `proxyConfiguration` | object | No | Apify Proxy | Browser-session proxy configuration |

Duplicate properties found through multiple URLs are emitted once per run.
The first matching source URL remains in `sourceUrl`.

#### Example input

```json
{
  "startUrls": [
    {
      "url": "https://propwire.com/search?filters=%7B%22locations%22%3A%5B%7B%22searchType%22%3A%22C%22%2C%22city%22%3A%22Chicago%20Heights%22%2C%22state%22%3A%22IL%22%2C%22title%22%3A%22Chicago%20Heights%2C%20IL%22%7D%5D%7D"
    }
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

### Output example

The following shortened record reflects the current output shape; example values are anonymized.

```json
{
  "propertyId": 123456,
  "sourceUrl": "https://propwire.com/search?filters=...",
  "propertyUrl": "https://propwire.com/realestate/property/123456",
  "fullAddress": "100 Sample Ave, Example City, IL, 60000",
  "address": {
    "address": "100 Sample Ave",
    "city": "Example City",
    "state": "IL",
    "zip": "60000"
  },
  "propertyType": "SFR",
  "bedrooms": 3,
  "bathrooms": 2,
  "yearBuilt": 1985,
  "buildingAreaSqFt": 1750,
  "livingAreaSqFt": 1600,
  "lotSizeSqFt": 7200,
  "estimatedValue": 245000,
  "estimatedEquity": 147000,
  "estimatedEquityPercent": 60,
  "lastSoldPrice": 180000,
  "lastSoldDate": "2023-05-15",
  "ownerName": null,
  "activeLeadTypes": ["absentee_owner", "high_equity"],
  "scrapedAt": "2026-01-15T12:00:00.000Z"
}
```

### How much does it cost to export PropWire property leads?

The Actor uses pay-per-event pricing:

- a **$0.005 start fee** for each run;
- one **Property lead** event for each unique dataset row.

Current per-property tiers are:

| Plan | Price per property |
| --- | ---: |
| Free | $0.000874 |
| Bronze | $0.000760 |
| Silver | $0.0005928 |
| Gold | $0.000456 |
| Platinum | $0.000304 |
| Diamond | $0.0002128 |

At the Free-tier event price, 100 properties cost about **$0.09** including the start fee, 1,000 cost about **$0.88**, and 10,000 cost about **$8.75**.
Your plan tier determines the final event amount.
Apify platform usage is handled according to the pricing shown in Console.
No event is charged for rejected, duplicate, or failed rows.

### Scheduling recurring pipeline refreshes

Create an Apify schedule with the same filtered search URL to refresh an acquisition list daily, weekly, or monthly.
Keep each run's dataset as a dated snapshot.
Then compare stable `propertyId` values between runs:

- IDs present only in the latest run are newly visible for that filter.
- IDs no longer present have left the current result set.
- Existing IDs can be compared for changed value, equity, sale, or lead flags.

The Actor returns snapshots; it does not itself send alerts or persist cross-run change state.
Use schedules, webhooks, and your destination system for that workflow.

### Export to spreadsheets and CRMs

From the dataset page, download CSV or Excel for spreadsheet review.
For repeated automation, use a webhook or integration to move accepted rows into:

- Google Sheets or Microsoft Excel;
- Airtable;
- a CRM staging table;
- Snowflake, BigQuery, or PostgreSQL;
- Make, Zapier, or n8n;
- an internal lead-scoring service.

Use `propertyId` as the source key when upserting records.
Use `sourceUrl` to preserve the acquisition segment that produced each row.

### Run through the Apify API

Set `APIFY_TOKEN` in your environment.
Do not commit tokens to source control.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~propwire-property-leads-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [{"url": "https://propwire.com/search?filters=YOUR_ENCODED_FILTERS"}],
    "maxItems": 100
  }'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/propwire-property-leads-scraper').call({
  startUrls: [{ url: 'https://propwire.com/search?filters=YOUR_ENCODED_FILTERS' }],
  maxItems: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/propwire-property-leads-scraper').call(run_input={
    'startUrls': [{'url': 'https://propwire.com/search?filters=YOUR_ENCODED_FILTERS'}],
    'maxItems': 100,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use Propwire Scraper with MCP and AI agents

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/propwire-property-leads-scraper"
```

#### Claude Desktop setup

Add this server object to the Claude Desktop MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/propwire-property-leads-scraper"
    }
  }
}
```

#### Cursor setup

Add the same `apify` server object under `mcpServers` in Cursor's MCP settings, then reload the available tools.

#### VS Code setup

Add the same HTTP MCP URL to your VS Code MCP configuration and enable the `automation-lab/propwire-property-leads-scraper` tool.

Example prompts:

- "Run the Propwire scraper for this Chicago Heights search URL and return 50 properties grouped by active lead type."
- "Export this high-equity PropWire search, then summarize estimated value and equity bands."
- "Run my saved Southeast preforeclosure search input and prepare the dataset for CRM import."

An AI agent should not make outreach or investment decisions solely from these records.
Review source data and apply your own compliance and underwriting checks.

### Reliability, retries, and proxy behavior

PropWire requires a coherent session across browser bootstrap and paginated requests.
The Actor keeps cookies, XSRF state, browser fingerprint, and proxy identity together.
It retries failed session bootstrap with a fresh page up to three times.

The default Apify datacenter proxy route was verified during implementation.
A US residential proxy route was also verified for bootstrap but is not an automatic fallback.
This avoids silently moving users to a higher-cost proxy path.

A challenge page, malformed response, or exhausted retry fails the run with a non-zero status.
It is not reported as a successful empty search.

### Limits and important behavior

- Only `https://propwire.com/search?filters=...` inputs are accepted.
- At least one location must exist in each encoded filter object.
- The run limit is 10,000 unique properties.
- Public source fields can be absent or null.
- Owner names are returned only when included in the anonymous public search response.
- Contact enrichment and skip tracing are not provided.
- Property detail pages are not fetched in this version.
- Results reflect PropWire at collection time and can change later.
- Source UI or anti-bot changes may require an Actor update.
- Large runs take longer because the source is paginated in a browser session.

### Responsible use and legality

Use this Actor only for lawful purposes and data you are permitted to process.
Follow PropWire's terms, Apify's terms, applicable privacy laws, consumer-protection rules, and rules governing real-estate marketing and outreach.

Property and ownership data can be inaccurate, incomplete, delayed, or associated with people who should not be contacted.
Do not use the output for discriminatory housing decisions, harassment, eligibility decisions, or automated decisions with legal or similarly significant effects.
Verify critical facts against authoritative records before acting.
Honor suppression lists, consent requirements, and do-not-call rules in downstream outreach.

### Troubleshooting

#### Why does my input fail immediately?

Confirm that the URL uses the `propwire.com` hostname, the `/search` path, and a valid JSON-encoded `filters` parameter with at least one location.
The easiest fix is to rebuild the search in PropWire and copy the full address-bar URL.

#### Why did the run fail instead of returning zero rows?

The Actor fails when it sees a challenge, invalid response, or expired session after bounded retries.
This prevents upstream blocking from being mistaken for a legitimate empty market.
Retry later or test another permitted proxy configuration.

#### Why is `ownerName` null?

The anonymous search response does not expose an owner name for every property.
The Actor preserves null rather than guessing or making a separately billed detail request.

#### Why are fewer rows returned than `maxItems`?

The filtered search may have ended, several searches may overlap, or duplicate PropWire IDs may have been removed.
Only unique accepted records are charged and saved.

### Frequently asked questions

#### Is this a PropWire API?

It provides an API-callable export workflow through Apify, but it is not an official PropWire API and is not affiliated with PropWire.
It reads the same public search data used by the website.

#### Do I need a PropWire login or app session?

No.
The supported workflow uses anonymous public search data.
The Actor does not accept or store PropWire credentials.

#### Can I use multiple searches in one run?

Yes.
Add several complete search URLs to `startUrls`.
The Actor processes them in order and deduplicates properties by ID.

#### Can it find new or changed leads?

Each run is a current snapshot.
Schedule repeated runs and compare datasets in your own workflow to detect new, removed, or changed rows.

#### Does it include phone numbers or email addresses?

No.
It does not perform skip tracing or contact enrichment.

#### Can I export more than 10,000 properties?

The current per-run maximum is 10,000.
Use narrower market segments or separate legitimate searches when you need bounded, reviewable batches.

### Related real-estate data Actors

- [Crexi Commercial Real Estate Scraper](https://apify.com/automation-lab/crexi-commercial-real-estate-scraper) for public US commercial listing research.
- [Redfin Real Estate Agents Scraper](https://apify.com/automation-lab/redfin-real-estate-agents-scraper) for public agent directory workflows.
- [Clark County Property Records Scraper](https://apify.com/automation-lab/clark-county-property-records-scraper) for a county-specific public records workflow.

These Actors cover different sources and record types; they do not merge automatically with PropWire output.

### Support

If a run fails, share the Apify run URL and a sanitized input URL through the Actor's support tab.
Do not post API tokens, private proxy credentials, or personal outreach lists.
Include whether the input works in PropWire and whether the failure is consistent or intermittent.

# Actor input Schema

## `startUrls` (type: `array`):

One or more complete https://propwire.com/search?filters=... URLs. Filters from each URL are preserved and results are deduplicated by PropWire property ID.

## `maxItems` (type: `integer`):

Maximum number of unique property lead records to save across all search URLs.

## `proxyConfiguration` (type: `object`):

Proxy used for the coherent browser session. Apify Proxy is enabled by default; the lower-cost datacenter route is normally sufficient.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://propwire.com/search?filters=%7B%22locations%22%3A%5B%7B%22searchType%22%3A%22C%22%2C%22city%22%3A%22Chicago%20Heights%22%2C%22state%22%3A%22IL%22%2C%22title%22%3A%22Chicago%20Heights%2C%20IL%22%7D%5D%7D"
    }
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

All unique PropWire property records collected by this run.

# 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 = {
    "startUrls": [
        {
            "url": "https://propwire.com/search?filters=%7B%22locations%22%3A%5B%7B%22searchType%22%3A%22C%22%2C%22city%22%3A%22Chicago%20Heights%22%2C%22state%22%3A%22IL%22%2C%22title%22%3A%22Chicago%20Heights%2C%20IL%22%7D%5D%7D"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/propwire-property-leads-scraper").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 = {
    "startUrls": [{ "url": "https://propwire.com/search?filters=%7B%22locations%22%3A%5B%7B%22searchType%22%3A%22C%22%2C%22city%22%3A%22Chicago%20Heights%22%2C%22state%22%3A%22IL%22%2C%22title%22%3A%22Chicago%20Heights%2C%20IL%22%7D%5D%7D" }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/propwire-property-leads-scraper").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 '{
  "startUrls": [
    {
      "url": "https://propwire.com/search?filters=%7B%22locations%22%3A%5B%7B%22searchType%22%3A%22C%22%2C%22city%22%3A%22Chicago%20Heights%22%2C%22state%22%3A%22IL%22%2C%22title%22%3A%22Chicago%20Heights%2C%20IL%22%7D%5D%7D"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/propwire-property-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/propwire-property-leads-scraper"
        }
    }
}

```

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/320h84G7Z0HJacROT/builds/AzItjZf4OdSgo48dr/openapi.json
