# Facebook Marketplace Leads Scraper (`fanndev/facebook-marketplace-leads-scraper`) Actor

Scrape Facebook Marketplace listings by city and category or by search term: title, price, location, posting date, age, condition, photos, delivery options and vehicle or property attributes. Optionally opens each listing page for the description, coordinates and postal code. No login, no proxy.

- **URL**: https://apify.com/fanndev/facebook-marketplace-leads-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

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

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

## Facebook Marketplace Leads Scraper

Scrape Facebook Marketplace listings by city and category, or by search term:
title, price, location, posting date, age, sold status, photos, delivery
options, and the vehicle or property attributes Facebook renders alongside them.

Built for used-car dealers, property brokers and anyone doing second-hand price
research.

No login, no cookies to paste, no proxy required.

### Read this before you buy: the seller's name is not public

Facebook withholds the seller's identity from signed-out visitors —
`marketplace_listing_seller` comes back `null` on both the feed and the listing
page. This actor reports `sellerName: null` rather than omitting the field, so
the absence is visible in your data instead of looking like a parse gap. **No
logged-out scraper can return it**, whatever its listing promises.

What you *do* get for locating a lead: city, state, the human-readable location
line, and — on detail pages — the postal code and approximate coordinates.

### Use the city slug, not a location id

The city is the segment in the Marketplace URL: `nyc`, `jakarta`, `london`,
`sydney`.

A **numeric** location id looks like it works and does not. Measured on the same
category feed:

| Input | Result |
| --- | --- |
| `/marketplace/nyc/vehicles` | New York and New Jersey listings |
| `/marketplace/109571519060768/vehicles` | HTTP 200, 24 listings, all **California** |

The id is accepted and ignored, and you get Facebook's default location
presented as yours. This actor rejects numeric entries with a warning instead of
scraping them into wrong data.

### What you get

| Field | Example |
| --- | --- |
| `title`, `customTitle` | `2013 Subaru BRZ` |
| `priceText`, `priceAmount` | `$200,000`, `200000.0` |
| `city`, `state`, `locationText` | `Hayward`, `CA`, `Hayward, California` |
| `createdAtIso`, `ageDays` | `2026-09-10T…`, `9` |
| `subtitles`, `vehicleMileage`, `vehicleYear` | `["50K miles"]`, `50000`, `2013` |
| `bedrooms`, `bathrooms` | for property listings |
| `deliveryTypes`, `isSold`, `photoUrl`, `listingUrl` | |

With **`fetchDetails`** on, each listing page adds `description`, `condition`,
`currency`, `latitude`, `longitude` and `postalCode` — none of which appear in
the feed — at one extra request per listing.

### Input example

```json
{
  "cities": ["nyc", "jakarta"],
  "categories": ["vehicles"],
  "minPrice": 5000,
  "maxPrice": 25000,
  "sortBy": "creation_time_descend",
  "maxAgeDays": 7,
  "maxListingsPerQuery": 120,
  "fetchDetails": true,
  "exportFormats": ["csv"]
}
```

### Which filters are real

| Filter | How it is applied |
| --- | --- |
| `minPrice` / `maxPrice` | sent to Facebook **and** enforced locally — verified honoured (a $20k–$40k request returned $20,000–$39,000) |
| `sortBy` | sent to Facebook — verified to change the result set |
| `radiusKm` | sent to Facebook |
| `maxAgeDays` | enforced **locally**, against each listing's own timestamp |
| `excludeSold`, `titleContains` | enforced locally |

`maxAgeDays` is deliberately not sent as Facebook's `daysSinceListed`
parameter: that parameter is accepted and then ignored — a request with
`daysSinceListed=1` returned byte-for-byte the same listings as one without it.
Filtering on the published `creation_time` is the only version of this filter
that is actually true.

### Running it on Apify: use a residential proxy for pagination

Facebook serves the **rendered first page** to any IP, including Apify's. But the
**first pagination request from a datacenter IP** comes back with
`Rate limit exceeded`, so a platform run with no proxy stops at the ~24 listings Facebook renders.

The rate limit is on Facebook's GraphQL endpoint, which every actor in this
family uses for its second page onwards. It was measured on 2026-09-20 with the
Ad Library actor, three runs of the same search within a minute:

| Run | Result |
| --- | --- |
| Apify, no proxy | 30 results, 1 page — log: `Rate limit exceeded` |
| Apify, `RESIDENTIAL` proxy | 60 results, 4 pages |
| Local machine, no proxy | 70 results, 5 pages |

So: switch the Apify proxy on and pick the **RESIDENTIAL** group whenever you
want more than the first page. Running from your own machine needs no proxy at
all.

The actor logs a warning naming the rate limit when it hits one, so a short run
is never silently mistaken for a short result set.

### Notes

- Listings with no readable price are **dropped** by a price filter rather than
  admitted on a guess.
- `photoUrl` is a signed CDN link and expires.
- `vehicleMileage`, `bedrooms` and `bathrooms` are parsed from Facebook's
  free-text attribute line (`"50K miles"`), which is the only place the feed
  publishes them. The original strings are kept in `subtitles`.
- Coordinates on detail pages are Facebook's approximate point for the listing,
  not the seller's address.

# Actor input Schema

## `cities` (type: `array`):

The city segment from a Marketplace URL: 'nyc', 'jakarta', 'london', 'sydney'. IMPORTANT: a numeric location id does NOT work - Facebook accepts it, returns HTTP 200, and hands back listings from its own default location instead of yours. Numeric entries are rejected with a warning rather than scraped into wrong data. Leave empty to use Facebook's default location.

## `categories` (type: `array`):

Marketplace category slugs, e.g. vehicles, propertyforsale, propertyrentals, electronics, furniture, apparel. Each is combined with every city.

## `searchQueries` (type: `array`):

Free-text searches, run inside each city. Use this instead of (or alongside) categories when you are after a specific item.

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

Marketplace item URLs or numeric item ids, to fetch the detail page of listings you already know about.

## `maxListingsPerQuery` (type: `integer`):

Facebook renders about 24 listings per page and this actor replays its pagination query for the rest.

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

Sent to Facebook as a URL filter (verified honoured) and also applied locally. Listings with no readable price are dropped, since an unknown price cannot be shown to sit inside the range.

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

Sent to Facebook as a URL filter (verified honoured) and also applied locally.

## `sortBy` (type: `string`):

Facebook's own sort. 'Newest first' is the one to use for lead generation.

## `radiusKm` (type: `integer`):

How far around the city to look.

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

Filtered locally against each listing's own timestamp. Facebook's daysSinceListed URL parameter is accepted and then ignored by Facebook - byte-for-byte identical results - so this actor does not rely on it.

## `excludeSold` (type: `boolean`):

Drop listings Facebook marks as sold.

## `titleContains` (type: `string`):

Case-insensitive substring match against the listing title.

## `fetchDetails` (type: `boolean`):

Adds the description, exact coordinates, postal code, condition, currency and vehicle specifications - none of which appear in the feed. Costs one extra request per listing.

## `maxDetailFetches` (type: `integer`):

Caps the extra requests when detail fetching is on.

## `emitSummary` (type: `boolean`):

Append one RUN\_SUMMARY record: listings collected, cities covered, and the min / median / max price.

## `exportFormats` (type: `array`):

Also write the results to the key-value store in these formats. The dataset is always produced regardless.

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

Apify Proxy. RESIDENTIAL is the default and you should keep it: Facebook serves datacentre addresses a reduced page with no feed and no counts, so a run without a residential proxy usually collects nothing. The exit country also decides which country's results you get.

## Actor input object example

```json
{
  "cities": [
    "nyc"
  ],
  "categories": [
    "vehicles"
  ],
  "maxListingsPerQuery": 48,
  "sortBy": "",
  "excludeSold": true,
  "fetchDetails": false,
  "maxDetailFetches": 50,
  "emitSummary": true,
  "exportFormats": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Marketplace listings per query, plus the run summary and error rows.

# 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 = {
    "cities": [
        "nyc"
    ],
    "categories": [
        "vehicles"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/facebook-marketplace-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 = {
    "cities": ["nyc"],
    "categories": ["vehicles"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fanndev/facebook-marketplace-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 '{
  "cities": [
    "nyc"
  ],
  "categories": [
    "vehicles"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call fanndev/facebook-marketplace-leads-scraper --silent --output-dataset

```

## MCP server setup

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