# For Sale By Owner (FSBO) Scraper (`steadyscrape/fsbo-scraper`) Actor

Scrape For Sale By Owner (FSBO) homes on ForSaleByOwner.com by city, county, state or ZIP: price, beds, baths, sq ft, address, photos, days listed, sold date, and whether the seller takes cash or owner financing. Active, under-contract and sold. Export to JSON, CSV or Excel, or via API.

- **URL**: https://apify.com/steadyscrape/fsbo-scraper.md
- **Developed by:** [Steady Scrape](https://apify.com/steadyscrape) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 fsbo listings

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## For Sale By Owner (FSBO) Scraper

Scrape **For Sale By Owner (FSBO) listings** from **ForSaleByOwner.com**, the largest FSBO site in the US, by **city, county,
state or ZIP code**. This FSBO scraper gives every home its owner listed, in one clean row: **asking price**, **bedrooms**,
**bathrooms**, **square feet**, **lot size**, **year built**, the **full address** split into street, city, county, state and
ZIP, **map coordinates**, the **description**, **photos**, **open houses**, the **date listed** and **days on the market**, and
the **seller's terms**: whether they accept **cash offers**, **owner financing** or a pre-approved mortgage, and whether they
work with buyer's agents.

Pick **active**, **under contract** and **sold** listings (with the **date sold**), filter by price, beds, baths, property type,
year built and living area, keep only sellers who offer **owner financing**, and schedule it to get only **new listings, price
cuts and status changes**. Export the data to JSON, CSV or Excel, or read it via API.

### What you can do with this FSBO scraper

- **Find motivated sellers**: real-estate investors and wholesalers list every owner selling without an agent in their area,
  with days on the market, price cuts and the seller's accepted offer types.
- **Find owner-financing and cash deals**: keep only sellers who say they accept owner financing or cash.
- **Prospect FSBO sellers**: agents and brokers get every new for-sale-by-owner listing in their farm area each morning.
- **Track the FSBO market**: asking prices, sold prices and dates, inventory per ZIP code, county or state.
- **Feed a CRM or a spreadsheet**: one flat row per home, with the listing link to open it.

### How it works

1. Enter one or more **locations**: `Houston, TX`, `Harris County, TX`, `Texas` or `TX`, a ZIP code such as `77049`, or paste
   a ForSaleByOwner.com search link (its filters are kept) or a single listing link.
2. Pick the **listing statuses** (for sale, under contract, sold) and any **filters**. The filters are sent to the site's own
   search, so you only pay for homes that match.
3. Run it. Each listing comes once per run, even when two of your locations overlap; a location that gives nothing returns one
   **free row** saying why (no such place, no listing with those filters, already delivered).

By default you get **homes sold by their owners only**. ForSaleByOwner.com also mixes builders' new-construction homes into its
results (163 of Houston's 198 "for sale" results on 2026-10-30); switch on **Include new construction** to get them too, with
the builder's name, the plan and the builder's listing link.

### Input example

```json
{
    "locations": ["Houston, TX", "Harris County, TX", "77049"],
    "listingStatuses": ["for_sale", "under_contract"],
    "propertyTypes": ["house", "multi"],
    "maxPrice": 350000,
    "minBedrooms": 3,
    "onlyOwnerFinancing": false,
    "maxListingsPerLocation": 1000,
    "onlyNewOrChanged": false
}
```

### Output: FSBO listing data

One row per home (arrays shortened here):

```json
{
    "listingId": "6acfd8017ef2a299a8093e16",
    "listingUrl": "https://www.forsalebyowner.com/listing/6215-Scotch-Pine-Drive-Houston-TX-77049/6acfd8017ef2a299a8093e16",
    "listingStatus": "for_sale",
    "isNewConstruction": false,
    "price": 225000,
    "isPriceReduced": null,
    "propertyType": "House",
    "bedrooms": 3,
    "bathrooms": 2,
    "livingAreaSqft": 1287,
    "lotSize": 7440,
    "lotSizeUnit": "sqft",
    "lotSizeSqft": 7440,
    "yearBuilt": 1978,
    "address": "6215 Scotch Pine Drive Houston, TX 77049",
    "street": "6215 Scotch Pine Drive",
    "city": "Houston",
    "county": "Harris",
    "state": "TX",
    "zip": "77049",
    "latitude": 29.8149941,
    "longitude": -95.1931589,
    "description": "Pine Trails is a charming neighborhood located in Harris County… Seller is willing to work a deal on Owner financing with a down payment.",
    "listedDate": "2026-10-14",
    "soldDate": null,
    "daysListed": 16,
    "acceptedOffers": ["cash", "owner financing"],
    "acceptsCashOffers": true,
    "acceptsOwnerFinancing": true,
    "buyerAgents": "buyers only",
    "appliances": ["Dishwasher", "Garbage Disposal", "Refrigerator", "Microwave"],
    "heating": ["Central Heating"],
    "cooling": ["Central AC"],
    "parking": ["Attached Garage", "On-street"],
    "rooms": ["Primary Bath", "Dining Room", "Family Room", "Walk-In Closet"],
    "fencedYard": true,
    "patio": true,
    "pool": null,
    "openHouses": [],
    "photoUrl": "https://images.forsalebyowner.com/1197de5e-60e3-4cf8-af30-edf62b03a107.curb-appeal-brick-home-and-lush-yard.png?1792019714",
    "photoCount": 18,
    "virtualTourUrl": null,
    "builderName": null,
    "rowType": "listing",
    "status": "ok",
    "input": "Houston, TX",
    "location": "Houston, TX",
    "position": 1,
    "change": null,
    "scrapedAt": "2026-10-30T07:00:00.000Z"
}
```

A location that gives nothing returns a free row instead:

```json
{
    "rowType": "status",
    "status": "not_found",
    "input": "Zzqxville, TX",
    "url": "https://www.forsalebyowner.com/search/list/zzqxville-texas",
    "reason": "ForSaleByOwner.com knows no place \"Zzqxville, TX\" (looked up as \"zzqxville-texas\"; the site has no such city, county or ZIP code). Check the spelling, or search the place on https://www.forsalebyowner.com and paste the search link."
}
```

### FSBO listing fields

| field | what it holds |
| --- | --- |
| `listingId`, `listingUrl` | The site's listing id and the listing's page. |
| `listingStatus` | `for_sale`, `under_contract` or `sold`. |
| `price`, `isPriceReduced` | Asking price in US dollars (for a sold home, the last asking price) and the site's price-cut flag. |
| `propertyType` | House, Condo, Townhouse, Multi-Family or Land. |
| `bedrooms`, `bathrooms`, `livingAreaSqft`, `yearBuilt`, `stories` | As the owner wrote them; half baths count as 0.5. |
| `lotSize`, `lotSizeUnit`, `lotSizeSqft` | The lot as written, its unit (`sqft` or `acres`) and the same lot in square feet. |
| `address`, `street`, `unit`, `city`, `county`, `state`, `zip`, `neighborhood` | The home's address, split. |
| `latitude`, `longitude` | The map pin. |
| `title`, `description` | The owner's headline and description, as plain text. |
| `listedDate`, `activatedDate`, `updatedDate`, `soldDate`, `daysListed` | Dates as `YYYY-MM-DD`; days from listing to the run (empty for sold homes). |
| `acceptedOffers`, `acceptsCashOffers`, `acceptsOwnerFinancing` | Offer types the seller accepts: cash, pre-approved mortgage, owner financing. |
| `buyerAgents` | `buyers only`, `agents welcome` or `agents welcome, commission offered`. |
| `leaseToOwn` | The seller offers lease-to-own. |
| `appliances`, `heating`, `cooling`, `parking`, `parkingSpaces`, `flooring`, `views`, `rooms` | Home details as the owner listed them. |
| `pool`, `fireplace`, `basement`, `fencedYard`, `patio`, `porch`, `deck`, `hotTub`, `jettedTub`, `securitySystem`, `sprinklerSystem`, `furnished`, `inLawSuite`, `waterfront` | `true` when the owner ticked it, `null` when they said nothing. |
| `openHouses` | Each open house's start and end (ISO 8601 UTC) and notes. |
| `photoUrl`, `photoUrls`, `photoCount`, `videos`, `virtualTourUrl` | Media. |
| `mlsNumber` | When the home is also on an MLS. |
| `isNewConstruction`, `builderName`, `planName`, `builderListingUrl`, `moveInDate` | Builders' new homes only (with **Include new construction**). |
| `input`, `location`, `position` | The location you entered, the place the site matched, and the place in its results. |
| `change`, `previousPrice`, `previousStatus` | With **Only new listings and changes**: `new`, `price_changed` or `status_changed`, with the earlier values. |
| `rowType`, `status`, `scrapedAt` | `listing` with status `ok` for a paid row; `status` for a free row, with status `no_results`, `not_found`, `invalid`, `duplicate`, `blocked` or `site_error`, plus its `url` and `reason`. When the row was made (ISO 8601 UTC). |

Owners are private people: the Actor never copies their names, pictures or their message to buyers. Descriptions come as the
site shows them: it masks phone numbers there as "(XXX) XXX-XXXX", but a few owners typed an email address or a phone number
the site did not mask (5 emails and 1 phone in 292 owner listings on 2026-10-30), and those come through as written. To reach a
seller, open `listingUrl`.

### Monitoring new FSBO listings and price cuts

Turn on **Only new listings and changes** and schedule the Actor (daily, for example). The first run delivers every listing; each
later run delivers only listings it has not seen, and listings whose price or status changed, with `previousPrice` and
`previousStatus`. Include **Under contract** and **Sold** in the statuses to see a home go under contract or sell. The memory is
kept per set of locations and filters, in your account's key-value store `steadyscrape-fsbo-state`.

### Pricing

**US$0.0015 per listing** (US$1.50 per 1,000 FSBO listings), with every field above included. Free rows (a place the site does
not know, a location with no listing, a duplicate, a listing link no longer online) cost nothing, and so do listings your filters
leave out. Set a maximum cost per run and the Actor stops when it reaches it.

### Limits

- One site: ForSaleByOwner.com. Its inventory is what owners list there (on 2026-10-30, Texas had 806 owner listings for sale,
  Florida 1,107, California 140; Houston 35); sold listings go back years.
- Some fields are only as complete as the owner made them: in 400 Florida listings, 340 gave a living area, 290 a lot size and
  391 a description. A few sold listings (13 of 393 in California) carry no sold date on the site itself.
- When the owner wrote a lot size without its unit, a value under 100 is read as acres and any other as square feet.
- Up to 9 for minimum bedrooms and bathrooms (the site's own filter takes one digit).

### Is it legal to scrape ForSaleByOwner.com?

This Actor reads only the listings owners publish for buyers, through the same public search the site's pages use, with no login,
and it never touches the "Contact the Seller" form. It leaves out the owners' names, pictures and messages. You are responsible for
how you use the data, including privacy, fair-housing and telemarketing rules where you work. This Actor is unofficial and is not
affiliated with, endorsed by or sponsored by ForSaleByOwner.com, FSBO.com or Rocket Companies.

### Feedback

Found a listing that comes out wrong, or need another field? Open an issue on the Actor's Issues tab.

# Actor input Schema

## `locations` (type: `array`):

One place or link per line: a city as <code>Houston, TX</code>, a county as <code>Harris County, TX</code>, a state (<code>Texas</code> or <code>TX</code>), a 5-digit ZIP code (<code>77049</code>), a ForSaleByOwner.com search link (<code>https://www.forsalebyowner.com/search/list/houston-texas</code>, whose filters are kept), or a single listing link (<code>https://www.forsalebyowner.com/listing/…/\<id></code>).

## `maxListingsPerLocation` (type: `integer`):

Stop each location after this many listings. 0 means every listing the site has for it.

## `listingStatuses` (type: `array`):

Which listings to scrape: active (for sale), under contract, and sold (with the date sold). Pick one or more.

## `includeNewConstruction` (type: `boolean`):

ForSaleByOwner.com mixes builders' new homes into its results (e.g. 163 of Houston's 198 "for sale" rows). Off by default, so you get homes sold by their owners only; on, builder rows come with the builder's name, plan and listing link.

## `propertyTypes` (type: `array`):

Leave empty for every type.

## `minPrice` (type: `integer`):

Leave empty for any price.

## `maxPrice` (type: `integer`):

Leave empty for any price.

## `minBedrooms` (type: `integer`):

0 to 9. Leave empty for any.

## `minBathrooms` (type: `integer`):

0 to 9. Leave empty for any.

## `minYearBuilt` (type: `integer`):

A year, e.g. <code>1990</code>. Leave empty for any.

## `maxYearBuilt` (type: `integer`):

A year. Leave empty for any.

## `minLivingArea` (type: `integer`):

Leave empty for any size.

## `maxLivingArea` (type: `integer`):

Leave empty for any size.

## `onlyOwnerFinancing` (type: `boolean`):

Keep only listings whose seller says they accept owner financing (16 of Houston's 35 owner listings on 2026-10-30). Applied to the listings read; one left out is not charged.

## `onlyNewOrChanged` (type: `boolean`):

For scheduled runs: deliver only listings no earlier run of the same locations and filters delivered, and listings whose price or status changed (with the earlier price and status). Include "Under contract" and "Sold" in the statuses to see a listing go under contract or sell. The memory lives in your account's key-value store <code>steadyscrape-fsbo-state</code>.

## Actor input object example

```json
{
  "locations": [
    "Houston, TX"
  ],
  "maxListingsPerLocation": 5,
  "listingStatuses": [
    "for_sale"
  ],
  "includeNewConstruction": false,
  "propertyTypes": [],
  "onlyOwnerFinancing": false,
  "onlyNewOrChanged": false
}
```

# Actor output Schema

## `listings` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "locations": [
        "Houston, TX"
    ],
    "maxListingsPerLocation": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyscrape/fsbo-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 = {
    "locations": ["Houston, TX"],
    "maxListingsPerLocation": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("steadyscrape/fsbo-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 '{
  "locations": [
    "Houston, TX"
  ],
  "maxListingsPerLocation": 5
}' |
apify call steadyscrape/fsbo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyscrape/fsbo-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/GluQaI9wKlVghG8rB/builds/viwi9bPCNgrxYYjTX/openapi.json
