# Facebook Marketplace Deal Finder (`kuantum/facebook-marketplace-scraper`) Actor

Find public Facebook Marketplace listings by keyword, city, area, or ZIP. Export Markdown, CSV, and Excel with prices, locations, sellers, images, history, filters, diagnostics, and alerts. Login-gated or blocked pages are reported transparently.

- **URL**: https://apify.com/kuantum/facebook-marketplace-scraper.md
- **Developed by:** [\[R\] Kuantum](https://apify.com/kuantum) (community)
- **Categories:** Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 listing collecteds

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?

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

## Kuantum Facebook Marketplace Scraper — Public Listings

This Actor extracts evidenced fields from publicly accessible Facebook Marketplace search and listing pages. It does not use login cookies, bypass access controls, or claim coverage when Facebook presents a login or bot-check wall.

Provide `keywords`, `areas`, and/or `zipCodes` to generate best-effort Marketplace searches, or provide explicit `searchUrls` and/or `listingUrls`. Each keyword is paired with each area and ZIP code. Search pages are used only to discover public `/marketplace/item/` links; direct listing pages are then inspected for title, price, currency, location, seller, description, and images when those fields are published in the page.

Every run writes the same headered results to the default dataset and to the Key-Value Store:

- `OUTPUT_MD` — Markdown table
- `OUTPUT_CSV` — CSV with column headers
- `OUTPUT_XLSX` — Excel workbook (`Listings` sheet) with column headers

The output columns are `listing_url`, `listing_id`, `source`, `search_keyword`, `search_area`, `search_zip_code`, `title`, `price`, `currency`, `location`, `seller_name`, `description`, `image_urls`, `date_published`, `contact_info`, `observed_at`, `first_seen_at`, `previous_price`, `price_change`, `collection_status`, and `error`. `date_published` and `contact_info` are populated only when Facebook exposes them publicly; the Actor does not guess or bypass access controls to obtain them.

When monetized, Apify charges one `listing-collected` event per usable listing row. Login, blocked, filtered, and failed rows are not charged. The run summary reports `chargedListings`.

Use `maxScrolls` to control how many additional times each search page is scrolled to discover more links. Set `infiniteScroll` to continue until `maxItems` is reached or two scrolls add no new links; it has a 100-scroll safety ceiling. `maxRetries`, `requestDelayMs`, `listingTimeoutMs`, and `maxConcurrency` control reliability and throughput. The circuit breaker is enabled by default: `stopOnAccessBlock` stops the run after one Facebook login, security, bot-check, 403, or 429 signal; use `maxBlockedPages` only when a higher threshold is deliberately needed. Each search page is saved as `SEARCH_###_HTML`, `SEARCH_###_SCREENSHOT`, and `SEARCH_###_SUMMARY` for diagnostics. `minPrice` and `maxPrice` optionally mark out-of-range listings as `filtered_out`. A named `marketplace-state` store tracks first-seen and prior prices across runs, allowing `previous_price` and `price_change` fields. `maxHistoryItems` bounds retained history. Set `alertWebhookUrl` to receive run-complete and price-drop JSON notifications; set `alertOnNewListing` to also receive new-listing events. `fallbackApiUrl` (and optionally `fallbackApiToken`) uses an authorized provider when Facebook discovery returns fewer than `maxItems`; it must return a JSON array or `{items: []}`. Login, bot-check, metadata, and navigation failures are included as rows with `collection_status` and `error`, and are also summarized in `FAILURES`.

An empty result caused by a login/access wall is a successful run with `outcome: "no_results_or_access_blocked"`; inspect the diagnostic HTML, screenshot, and `FAILURES` entries.

### Authorized Facebook session

The Actor can load session state from the protected `facebook-session` Key-Value Store when `useFacebookSession` is enabled. Capture it from an existing signed-in Chrome session with `node scripts/capture-facebook-session.mjs`; use the printed store ID as `facebookSessionStoreId` in Actor input. The script never asks for or stores your Facebook password. Session state expires and must be renewed when Facebook requests verification.

For a local authorized search and export, close the dedicated Chrome profile and run:

```bash
node scripts/search-marketplace.mjs --keyword "Michael Jordan card" --area Orlando --max-items 25 --infinite
```

When cloud search returns no listing links, use `node scripts/capture-marketplace-search.mjs` with `SEARCH_URL` to collect authorized search results from the signed-in browser. It stores listing URLs only under `CAPTURED_LISTING_URLS`; pass that store ID as `capturedListingStoreId` to the Actor. The Actor then enriches those URLs in the cloud.

### Scheduling

Apify schedules are account-level resources. Create one deliberately with:

```bash
APIFY_TOKEN="$APIFY_TOKEN" ACTOR_ID=bczOsNg3SNbHUqjHm CRON_EXPRESSION="0 * * * *" TIMEZONE="America/New_York" node scripts/create-schedule.mjs
```

Use `SCHEDULE_INPUT_JSON` to provide the normal Actor input. The script defaults to hourly execution; review the schedule and expected Apify costs before enabling it.

Use only URLs and data you are authorized to observe and process, and follow Facebook’s terms and applicable law.

# Actor input Schema

## `keywords` (type: `array`):

Terms to search for in Facebook Marketplace. Generated search URLs are best-effort because Facebook may require login.

## `areas` (type: `array`):

City, county, or area names paired with each keyword.

## `zipCodes` (type: `array`):

ZIP or postal codes paired with each keyword and area.

## `searchUrls` (type: `array`):

Public Facebook Marketplace search URLs. Login-required pages produce a failure record.

## `listingUrls` (type: `array`):

Direct public Facebook Marketplace listing URLs.

## `capturedListingStoreId` (type: `string`):

Optional protected Key-Value Store ID containing CAPTURED\_LISTING\_URLS created by the browser-assisted search capture script.

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

Maximum number of public listing pages to inspect and emit.

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

Number of public listing pages to inspect in parallel.

## `maxScrolls` (type: `integer`):

Additional scrolls per search page to load more listing links. Ignored when infinite scrolling is enabled.

## `infiniteScroll` (type: `boolean`):

Continue scrolling until maxItems is reached or two consecutive scrolls add no new listing links. A safety ceiling of 100 scrolls applies.

## `scrollDelayMs` (type: `integer`):

Wait time between infinite-scroll steps.

## `maxRetries` (type: `integer`):

Retries after navigation failures, using exponential backoff.

## `requestDelayMs` (type: `integer`):

Delay between page requests. Increase this to reduce request pressure.

## `stopOnAccessBlock` (type: `boolean`):

Stop the entire run after the configured number of Facebook login, security, bot-check, 403, or 429 signals.

## `maxBlockedPages` (type: `integer`):

Number of Facebook block signals allowed before the circuit breaker stops the run.

## `listingTimeoutMs` (type: `integer`):

Maximum time to wait for each listing page before recording a failure and continuing.

## `minPrice` (type: `number`):

Optional minimum listing price. Listings without a numeric price are filtered out when this is set.

## `maxPrice` (type: `number`):

Optional maximum listing price. Listings without a numeric price are filtered out when this is set.

## `alertWebhookUrl` (type: `string`):

Optional HTTPS webhook URL for run-complete and price-drop JSON notifications.

## `alertOnPriceDrop` (type: `boolean`):

Send a webhook notification when a previously seen listing price decreases.

## `alertOnNewListing` (type: `boolean`):

Send a webhook notification for listings not present in historical state.

## `maxHistoryItems` (type: `integer`):

Maximum number of listing records retained in persistent history.

## `fallbackApiUrl` (type: `string`):

Optional HTTPS endpoint you are authorized to use. It must return an array or {items: \[]} of listing objects.

## `fallbackApiToken` (type: `string`):

Optional bearer token for the authorized fallback API. It is never logged.

## `useApifyProxy` (type: `boolean`):

Route browser traffic through Apify Proxy when enabled. Disabled by default because direct access is more reliable for Marketplace.

## `useFacebookSession` (type: `boolean`):

Load Playwright session state from the protected facebook-session Key-Value Store. You must create and authorize this session yourself.

## `facebookSessionStoreId` (type: `string`):

Protected Key-Value Store ID containing PLAYWRIGHT\_STORAGE\_STATE. Use the ID printed by the session capture script.

## Actor input object example

```json
{
  "keywords": [],
  "areas": [],
  "zipCodes": [],
  "searchUrls": [],
  "listingUrls": [],
  "maxItems": 10,
  "maxConcurrency": 1,
  "maxScrolls": 3,
  "infiniteScroll": false,
  "scrollDelayMs": 1500,
  "maxRetries": 0,
  "requestDelayMs": 10000,
  "stopOnAccessBlock": true,
  "maxBlockedPages": 1,
  "listingTimeoutMs": 20000,
  "alertOnPriceDrop": true,
  "alertOnNewListing": false,
  "maxHistoryItems": 10000,
  "useApifyProxy": false,
  "useFacebookSession": false
}
```

# Actor output Schema

## `listings` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("kuantum/facebook-marketplace-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("kuantum/facebook-marketplace-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 '{}' |
apify call kuantum/facebook-marketplace-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kuantum/facebook-marketplace-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/bczOsNg3SNbHUqjHm/builds/QUfVuTerR33FV4ddj/openapi.json
