# Craigslist FSBO & Rentals Scraper + Monitor (`khadinakbar/craigslist-fsbo-rentals-scraper`) Actor

Collect owner-filtered sales and rentals with full details, new-listing and price/title monitoring, possible repost hints and deeper price-range searches. California defaults; other regions supported. Platform usage included.

- **URL**: https://apify.com/khadinakbar/craigslist-fsbo-rentals-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 property card scrapeds

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

## Craigslist FSBO & Rentals Scraper

Collect owner-filtered real estate sales and apartments/housing rentals into one structured dataset. An empty input searches six California markets: SF Bay Area, Los Angeles, San Diego, Sacramento, Orange County and Inland Empire. Replace the region list to search other Craigslist markets.

This is a private candidate with local parser, input, routing and billing tests. The development verification report records exact-build live test status. Human terms and data-use review is required before Store-ready handoff.

### Choose the right search

Use `listingMode: "both"` to compare sale inventory and rentals in the selected regions, `"fsbo"` for owner-filtered real estate sales, or `"rentals"` for apartments and housing for rent. FSBO means that Craigslist's owner filter was applied and retained. It is not independent verification of ownership or a guarantee that a listing is accurate.

A property researcher can select Sacramento, cap output at 20, and export the dataset as CSV. The `listingType` column separates sale and rental rows. Open the source URL to verify availability and property details before acting on a listing.

Rooms, sublets, vacation rentals, housing-wanted posts and commercial searches are outside this Actor's scope. Public HTML is scanned through bounded price-range continuation. Monitoring compares repeated runs; it does not create schedules, send alerts, discover contacts or promise complete statewide inventory.

### Input examples

California defaults:

```json
{ "listingMode": "both", "maxResults": 25, "includeDetails": false }
```

Separate sale and rental budgets in Sacramento:

```json
{
    "sites": ["sacramento"],
    "listingMode": "both",
    "minSalePrice": 100000,
    "maxSalePrice": 600000,
    "minRent": 1000,
    "maxRent": 3000,
    "maxResults": 20,
    "includeDetails": true
}
```

Another region:

```json
{ "sites": ["newyork"], "listingMode": "rentals", "maxResults": 10 }
```

`startUrls` overrides generated searches. Only Craigslist `rea` and `apa` search URLs and housing listing URLs are accepted. A direct sale search without `purveyor=owner` is labeled `sale`, not `fsbo`. A direct listing with no category context is labeled `unknown`.

### Housing analysis included

`pricePerSqft` divides a positive published asking amount by positive published area. `dataQualityFlags` exposes missing price/area and explicit rental wording in owner-filtered sale posts. Labels retain source classification; these flags do not verify ownership or fraud.

`PRICE_SNAPSHOT` reports observed counts, minimum, maximum and median asking amounts separately for each region, listing type and currency. It describes the delivered sample only. Zero/unknown prices and possible rental posts in sale categories are excluded from the summary.

### Output fields

| Field                                     | Meaning                                      |
| ----------------------------------------- | -------------------------------------------- |
| `listingId`, `url`, `title`               | Source listing identity and title            |
| `listingType`                             | `fsbo`, `rental`, `sale`, or `unknown`       |
| `ownerFilterApplied`                      | Owner-only source search retained its filter |
| `priceText`, `priceNumeric`, `currency`   | Published price and parsed amount            |
| `site`, `category`, `neighborhood`        | Craigslist source region and category        |
| `bedrooms`, `bathrooms`, `sqft`           | Housing fields when published                |
| `postedAt`, `scrapedAt`, `source`         | Source date, retrieval time and provenance   |
| `description`, `images`, `address`, `geo` | Optional detail enrichment when published    |
| `detailEnriched`                          | Whether the full listing detail was obtained |

Missing source fields are omitted; zero prices are retained. Coordinates can be approximate. The illustrative shape below is synthetic, not a live acceptance sample:

```json
{
    "listingId": "ExampleToken",
    "title": "Example owner-listed house",
    "url": "https://www.craigslist.org/view/d/example/ExampleToken",
    "site": "sacramento",
    "category": "rea",
    "listingType": "fsbo",
    "ownerFilterApplied": true,
    "priceNumeric": 450000,
    "currency": "USD",
    "detailEnriched": false,
    "source": "craigslist.org",
    "scrapedAt": "2026-10-14T12:00:00.000Z"
}
```

`OUTPUT` contains the outcome, persisted count, named charge counts and warnings. `RUN_SUMMARY` adds region/category coverage, request counts, skipped searches, deadlines and failure diagnostics. `DELIVERY_STATE` stores bounded reconciliation metadata; a resumed run with an unresolved write or changed input/build stops rather than replaying an ambiguous charge.

### Pay per event and platform usage

Events: $0.0032 startup per memory GB (minimum one); $0.00025 per delivered card or detailed listing (one event per row); $0.00015 per search request attempted, including continuation, empty searches and unsuccessful responses. Platform compute, transfer and storage usage are included in these event fees. Unchanged monitoring listings are not charged as results; startup and attempted-search fees still apply.

For 25 cards or detailed listings, result events total $0.00625. Add startup and attempted-search fees; platform usage is included. `maxResults` limits result events, not total run cost. Use Apify's run charge ceiling as well. One serialized batched writer respects the remaining named-event allowance, persists pending intent before writes and checks the accepted named event counts afterward.

### Coverage and best results

The result cap is divided across selected region/type searches. With default input there are twelve searches and twenty-five result slots. Unused slots are not moved between searches. A small cap may skip later regions; the summary records this. Choose one or two regions for deeper coverage.

Each price-range query exposes roughly 350–360 public cards; further narrower ranges can broaden coverage. A result quota, truncated source page, upstream failure, or time budget can produce `PARTIAL`; it is not a full inventory scan. `VALID_EMPTY` requires an explicit empty-source marker with empty listing metadata. A challenge, failed request, missing owner filter or unrecognized markup is not counted as empty success.

The default uses direct HTTPS at 256 MB with a bounded 96 MB JavaScript heap, at most two simultaneous detail fetches and an eight-second request timeout. Platform usage is included; paid proxy routes are unavailable. After two required request failures, the circuit breaker stops additional network work. Response bodies are bounded to 1 MiB. No access challenges are solved or unreviewed redirects followed.

### API and AI-agent usage

After the private build is available, an account with access can start a bounded run:

```bash
curl --request POST \
  --url 'https://api.apify.com/v2/acts/khadinakbar~craigslist-fsbo-rentals-scraper/runs?maxTotalChargeUsd=0.25' \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"sites":["sacramento"],"listingMode":"both","maxResults":10}'
```

Read the returned run ID, wait for its terminal status, then retrieve its default dataset and the `OUTPUT` and `RUN_SUMMARY` key-value records. A successful process status alone does not prove useful or complete output.

Agent prompt: “Search Sacramento Craigslist FSBO and rentals, up to ten listings. Keep sale and rental prices separate, return source URLs, and report whether the run was complete, partial, or empty. Do not infer ownership or unpublished property fields.” Hosted MCP execution has not been independently verified for this candidate.

### Failure recovery

`INVALID_INPUT` identifies a setting the caller can correct. `CONFIG_ERROR` means the connection or stored reconciliation state needs attention. `UPSTREAM_FAILED` means no usable listing was retrieved. `PARTIAL` preserves useful rows with incomplete coverage. Inspect the summary before retrying; do not repeatedly retry challenges or unresolved writes.

### Builder's note and data-use boundary

This focused Actor reuses the housing parser from a fresh pull of the general Craigslist housing Actor. The main engineering changes are balanced FSBO/rental quotas, retained-owner-filter checks, separate price filters, direct-first extraction, cap-aware dispatch, source-quality analysis and observed price summaries. Its general-purpose sibling is [Craigslist Real Estate & Housing Scraper](https://apify.com/khadinakbar/craigslist-real-estate-scraper), which supports additional housing categories.

Craigslist's [Terms of Use](https://www.craigslist.org/about/terms.of.use) restrict automated collection without a separate license. Obtain appropriate written authorization before live collection. The production workflow requires human review of collection authority and data use before Store-ready handoff. This candidate does not harvest reply-form contacts, contact sellers, or publish listings. Retain exported data only as permitted by your authorization and delete it when that purpose ends. This Actor is not affiliated with Craigslist.

Development status dated October 14, 2026: private technical validation is recorded separately from human data-use review and Store release approval. Prior sibling runs do not validate this build.

### Monitoring and deeper coverage

Use `monitorMode: "new"` to return new listings only, or `monitorMode: "changes"` to include new listings and asking-price/title changes. Set a stable `watchName` and keep query filters unchanged. The first run establishes the baseline. Later runs attach `changeTypes`, `previousPrice`, `previousTitle` and `priceChangePercent`. Matching titles/region/type/neighborhood under a new listing ID produce a **possible repost** hint, not a verified repost verdict. Example:

```json
{
    "sites": ["sfbay"],
    "listingMode": "rentals",
    "maxResults": 100,
    "monitorMode": "changes",
    "watchName": "bay-rentals",
    "maxSearchPages": 8
}
```

Run the same input again to check changes. You can attach it to your own Apify task schedule; this Actor does not create schedules or send messages. State is isolated by authenticated account, Actor, watch name and actual query scope. A server-managed queue lock rejects overlapping runs of the same watch. History expires after 60 days and retains at most 10,000 observations. Partial scans preserve older unseen entries; no disappearance or removal claims are made. Uncertain commits fail closed. Use a fresh run after interruption; resurrection/resume of an existing delivery is deliberately rejected.

`maxResults` supports up to 5,000 delivered records, `maxSearchPages` up to 30 search attempts and `maxListingsScanned` up to 10,000 observations. Each search window has roughly 350–360 static cards. Crowded windows are split into **narrower price ranges with a shared boundary deduplicated by listing ID**, preserving filters and removing duplicate listing IDs. This can reach older results beyond one public window. Identical-price groups, missing prices, source changes, time and page limits can still truncate coverage. It does not promise a complete Craigslist inventory or rely on ignored page offsets. `RUN_SUMMARY` reports search pages, price partitions, scanned/unchanged counts and incomplete coverage.

### Usage-included price examples

At the configured prices: 25 listings across the default twelve California searches cost at most $0.01125 in event fees when all twelve searches are attempted; 1,000 listings with twelve search requests cost $0.255. Both cards and full details use the same result fee. A one-listing single-search run costs $0.00360, and a one-search no-change monitor costs $0.00335. These are event-fee examples, not a guarantee of all future margins. Tiny and no-change runs can cost more than competitors without startup fees. Details are returned only when successfully fetched; any useful card fallback is labeled without pretending detail enrichment succeeded.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/craigslist-fsbo-rentals-scraper/changelog.md

# Actor input Schema

## `connectionMode` (type: `string`):

Direct HTTPS with platform usage included. Paid proxy routes are not supported in this version.

## `listingMode` (type: `string`):

Choose owner-filtered real estate sales, apartment/housing rentals, or both. FSBO uses the Craigslist owner filter; this is not independent proof of property ownership. Both types share maxResults.

## `sites` (type: `array`):

Search these Craigslist regions. Defaults to SF Bay Area, Los Angeles, San Diego, Sacramento, Orange County and Inland Empire. Replace with other region codes for broader coverage; use 1–30 regions.

## `maxResults` (type: `integer`):

Maximum delivered records, up to 5,000. Deeper searches split crowded price ranges; source caps and scan/page limits can leave partial coverage.

## `includeDetails` (type: `boolean`):

Open each listing for description, photos, address and housing attributes when published. Enriched rows cost $0.00015; cards and recoverable card-only fallbacks cost $0.0001. Derived price-per-square-foot and source-quality flags are included. Platform usage is separate.

## `searchQuery` (type: `string`):

Optional free-text keywords Craigslist applies to titles and post bodies (e.g. studio, backyard, downtown).

## `minSalePrice` (type: `integer`):

Minimum asking price for FSBO searches. Zero disables the filter.

## `maxSalePrice` (type: `integer`):

Maximum asking price for FSBO searches. Zero disables the filter.

## `minRent` (type: `integer`):

Minimum rent for rental searches. Zero disables the filter.

## `maxRent` (type: `integer`):

Maximum rent for rental searches. Zero disables the filter.

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

Filter search results to at least this many bedrooms. Use 0 to disable.

## `maxBedrooms` (type: `integer`):

Filter search results to at most this many bedrooms. Use 0 to disable.

## `minBathrooms` (type: `number`):

Filter search results to at least this many bathrooms. Use 0 to disable.

## `maxBathrooms` (type: `number`):

Filter search results to at most this many bathrooms. Use 0 to disable.

## `minSqft` (type: `integer`):

Filter by minimum living area when Craigslist exposes sqft. Use 0 to disable.

## `maxSqft` (type: `integer`):

Filter by maximum living area when Craigslist exposes sqft. Use 0 to disable.

## `housingTypes` (type: `array`):

Optional Craigslist housing_type filters (apartment, condo, house, townhouse, etc.). Leave empty for all types.

## `postalCode` (type: `string`):

Optional ZIP/postal that redirects Craigslist to a city search scoped around that code.

## `searchRadiusMiles` (type: `integer`):

Radius in miles around postalCode. Only applied when postalCode is set.

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

Optional Craigslist real estate (rea) or apartment rental (apa) search/listing URLs. Overrides regions and listingMode. Sale URLs without the owner filter are labeled sale, not FSBO. No other housing category is supported.

## `maxRunSeconds` (type: `integer`):

Stop dispatching work at this time budget. Completed requests and persisted listings remain available; unfinished work is reported as PARTIAL.

## `site` (type: `string`):

Optional single region that overrides sites when nonempty. Leave empty for the California default regions.

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

Legacy minimum price applied to both types unless their specific minimum is set. Prefer minSalePrice and minRent.

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

Legacy maximum price applied to both types unless their specific maximum is set. Prefer maxSalePrice and maxRent.

## `catsOk` (type: `boolean`):

Only return listings marked cats-ok on Craigslist.

## `dogsOk` (type: `boolean`):

Only return listings marked dogs-ok on Craigslist.

## `furnishedOnly` (type: `boolean`):

Only return furnished listings when Craigslist supports the filter.

## `hasPic` (type: `boolean`):

Only return listings that include photos.

## `postedToday` (type: `boolean`):

Only return listings Craigslist marks as posted today.

## `maxAgeDays` (type: `integer`):

Skip listings older than this many days when a posted date is available. Use 0 to disable.

## `maxConcurrency` (type: `integer`):

Parallel HTTP requests (1–5). Requests are also limited to 30 per minute.

## `monitorMode` (type: `string`):

First monitoring run establishes history and returns new listings. Later runs compare the same watch and query scope; unchanged rows are not billed.

## `watchName` (type: `string`):

Required for monitoring unless run as a saved task. Keep this name and query scope unchanged between runs. Isolated by authenticated account and Actor. History expires after 60 days and is bounded to 10,000 observations.

## `maxSearchPages` (type: `integer`):

Hard bound on initial searches plus price-range continuation. Each attempted search costs $0.00015 including empty or blocked responses. This caps work; it is not a promise of complete inventory.

## `maxListingsScanned` (type: `integer`):

Monitoring scans unchanged listings to find updates; stops at this bound even when fewer records are delivered.

## Actor input object example

```json
{
  "connectionMode": "direct",
  "listingMode": "both",
  "sites": [
    "sfbay",
    "losangeles",
    "sandiego",
    "sacramento",
    "orangecounty",
    "inlandempire"
  ],
  "maxResults": 25,
  "includeDetails": false,
  "searchQuery": "studio",
  "minSalePrice": 0,
  "maxSalePrice": 0,
  "minRent": 0,
  "maxRent": 0,
  "minBedrooms": 0,
  "maxBedrooms": 0,
  "minBathrooms": 0,
  "maxBathrooms": 0,
  "minSqft": 0,
  "maxSqft": 0,
  "housingTypes": [],
  "postalCode": "94110",
  "searchRadiusMiles": 0,
  "startUrls": [],
  "maxRunSeconds": 180,
  "site": "sfbay",
  "minPrice": 0,
  "maxPrice": 0,
  "catsOk": false,
  "dogsOk": false,
  "furnishedOnly": false,
  "hasPic": false,
  "postedToday": false,
  "maxAgeDays": 0,
  "maxConcurrency": 2,
  "monitorMode": "off",
  "maxSearchPages": 12,
  "maxListingsScanned": 5000
}
```

# Actor output Schema

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

All scraped housing listings as structured JSON records.

## `OUTPUT` (type: `string`):

Terminal outcome, itemsPushed, and chargedEventCounts.

## `RUN_SUMMARY` (type: `string`):

Detailed run diagnostics and outcome.

## `PRICE_SNAPSHOT` (type: `string`):

Bounded-sample counts and medians by region, sale/rental source type and currency; not market value.

# 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 = {
    "listingMode": "both",
    "sites": [
        "sfbay",
        "losangeles",
        "sandiego",
        "sacramento",
        "orangecounty",
        "inlandempire"
    ],
    "maxResults": 25,
    "includeDetails": false,
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/craigslist-fsbo-rentals-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 = {
    "listingMode": "both",
    "sites": [
        "sfbay",
        "losangeles",
        "sandiego",
        "sacramento",
        "orangecounty",
        "inlandempire",
    ],
    "maxResults": 25,
    "includeDetails": False,
    "startUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/craigslist-fsbo-rentals-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 '{
  "listingMode": "both",
  "sites": [
    "sfbay",
    "losangeles",
    "sandiego",
    "sacramento",
    "orangecounty",
    "inlandempire"
  ],
  "maxResults": 25,
  "includeDetails": false,
  "startUrls": []
}' |
apify call khadinakbar/craigslist-fsbo-rentals-scraper --silent --output-dataset

```

## MCP server setup

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