Facebook Marketplace Scraper — Search Results Export avatar

Facebook Marketplace Scraper — Search Results Export

Pricing

Pay per event

Go to Apify Store
Facebook Marketplace Scraper — Search Results Export

Facebook Marketplace Scraper — Search Results Export

Export public Facebook Marketplace search results — title, price, seller, city/state, and delivery type — for one or more search queries and a buy-location. No login, no browser automation. Deduplicated listings; pay only for results that land.

Pricing

Pay per event

Rating

0.0

(0)

Developer

DevilScrapes

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 hours ago

Last modified

Share


Give this Actor a search term (bike, sofa, whatever your buyers are hunting for) and a buy-location, and it walks Marketplace's own search results the same way a logged-out browser would — talking to the internal GraphQL endpoint the search page itself loads, handling the persisted-query id Meta rotates on every deploy, and handing you back structured JSON instead of a page you'd have to scroll and screenshot yourself. Pricing is per listing landed — a narrow query at a quiet location still costs you only the flat start fee.

🎯 What this scrapes

Public Facebook Marketplace search results — the same listings you'd see running that search logged out. For each queries entry we:

  • Search Marketplace from your chosen latitude/longitude/radius_km.
  • Walk the search results page by page, up to max_pages_per_query.
  • Emit one row per listing: title, price, seller, location, and delivery options.

Listing detail pages, seller profiles, messaging, and category browsing without a search term are out of scope — see Limitations below.

🔥 Features

  • 🛡️ We rotate browser fingerprints via curl-cffi impersonation so Facebook's servers see a real browser's TLS handshake, not a bare Python client.
  • 🔁 We retry with exponential backoff on 408/429/503 and network errors — up to 5 attempts per request, honouring Retry-After when Facebook sends it.
  • 🔧 We self-heal the persisted-query id. Meta rotates the internal doc_id this endpoint depends on with every deploy; when a search comes back with a hard error, we re-derive the current id straight from the search page's own JS bundles, cache it, and retry — no manual patch, no waiting on us to ship a fix.
  • 🧱 We isolate failures per query. A search term that fails doesn't take down the rest of your batch — it's logged and reported on its own, and the run keeps going.
  • 🧊 We keep the dataset clean and deduplicated — Pydantic-validated rows, ISO-8601 timestamps, and the same listing never billed twice even if it turns up under two different search terms in one run.
  • 💰 You pay only for results that land. A quiet search that genuinely finds nothing costs only the small actor-start warm-up fee — never billed per listing for listings that weren't there.

🧭 Use cases

  • Resale/arbitrage sourcing — track new listings for a product category across one or more buy-locations to spot underpriced inventory fast.
  • Local market research — see what's actually selling (and for how much) in a category near a given city, without manually paging through Marketplace.
  • Price monitoring — watch a search term's price spread over time to inform your own listing price.
  • Lead generation for local services — find people selling items that signal a need (moving boxes, old furniture, used equipment) in your service area.

⚙️ How to use it

  1. Click Try for free (or run via API/CLI) and open the input form.
  2. Enter one or more queries — the search terms you'd type into Marketplace yourself.
  3. Set latitude/longitude/radius_km to the buy-location you want results centered on. Defaults to New York City, 500 km radius.
  4. Set max_pages_per_query if you want fewer or more results than the default 3 pages per search.
  5. Leave proxy_configuration on its default (Residential, US). Facebook serves a login wall to datacenter IPs, so this one is required rather than optional — we measured it.
  6. Run it. Each query is processed in turn; results land in the dataset as they're scraped, so you can start exporting before the run finishes.

📥 Input

FieldTypeDefaultNotes
queriesarray of stringRequired. One or more Marketplace search terms.
latitudenumber40.7128 (NYC)Buy-location latitude, -90..90.
longitudenumber-74.0060 (NYC)Buy-location longitude, -180..180.
radius_kminteger500Search radius around the buy-location, 1..500.
max_pages_per_queryinteger3Stop paginating a query's results after this many pages, 1..50.
proxy_configurationobjectResidential, USApify Proxy configuration. Measured in the cloud: the plain runner IP and the shared datacenter pool both get Facebook's login wall; Residential/US returns real listings.
{
"queries": ["bike", "sofa"],
"latitude": 40.7128,
"longitude": -74.0060,
"radius_km": 500,
"max_pages_per_query": 2,
"proxy_configuration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}

📤 Output

FieldTypeNotes
querystringEcho of the input search query this listing was found under.
listing_idstringFacebook Marketplace listing id.
titlestringListing title.
listing_urlstringConstructed link to the listing on Marketplace.
price_formatted / price_amountstring | null / number | nullHuman-readable and numeric listing price.
strikethrough_price_formattedstring | nullOriginal (pre-discount) price, when the seller shows one.
creation_timestring (ISO-8601)Listing creation time.
city / statestring | nullReverse-geocoded listing location.
seller_name / seller_idstring | nullSeller info — sometimes null; Marketplace occasionally omits the seller block entirely.
delivery_typesarray of stringe.g. ["IN_PERSON", "PUBLIC_MEETUP"].
{
"query": "bike",
"listing_id": "1378988563664222",
"title": "Lightweight & Comfortable City Bike - $160",
"listing_url": "https://www.facebook.com/marketplace/item/1378988563664222/",
"price_formatted": "PLN160",
"price_amount": 160.0,
"strikethrough_price_formatted": "PLN190",
"creation_time": "2026-08-19T00:00:00+00:00",
"city": "Palo Alto",
"state": "CA",
"seller_name": null,
"seller_id": null,
"delivery_types": ["IN_PERSON", "PUBLIC_MEETUP"]
}

💰 Pricing

Pay-per-event. No subscription, no seat fee — you're billed for a run's warm-up and for every listing that actually lands in your dataset.

EventPriceTrigger
actor-start$0.20Once per run, the first time any query's search results are successfully read (whether that yields listings or a genuine zero-result search).
listing-scraped$0.005Once per unique listing written to the dataset.

At the default settings that works out to $5.20 per 1,000 listings ($0.20 start + 1,000 × $0.005). A search that genuinely finds nothing still costs the $0.20 start fee and nothing more — you're never billed per listing for listings that weren't there. The same listing turning up under two different search terms in one run is billed once, not twice.

🚧 Limitations

  • Public Marketplace search results only. No login, no cookies beyond the anonymous bootstrap request, and no account of ours is used to reach anything.
  • No listing detail pages. You get the primary thumbnail's data path, not the full photo gallery, seller profile, or full description text.
  • No messaging or making offers. Purely a read-only export of what's already publicly visible.
  • No category browsing without a search term, and no saved searches. Every run needs at least one queries entry.
  • No historical/date-range seeking. The Actor walks forward from the current search cursor only; it doesn't jump to an arbitrary past date.
  • Result density varies. A narrow query/location combination can legitimately return one listing, or zero — that's a real, successful answer, not a failure, and it's priced accordingly (you only pay for what lands).
  • If Facebook's servers can't be reached at all, or the search page comes back as a login wall/challenge, that query is reported as failed rather than as an empty (but successful) result — the two are never conflated.

❓ FAQ

Does this need a Facebook account or access token? No. It reads the same public search results anyone can see logged out.

Can it scrape listing detail pages, seller profiles, or messages? No — search results only. See Limitations.

Why did one of my queries come back with zero rows? Either that search genuinely has no matching listings right now near your buy-location (a real, successful zero), or the search page didn't resolve at all (login wall/challenge) — the run's status message tells you which, per query.

What proxy should I use? Residential, pinned to the US — and that is the default, so you do not need to do anything. We measured it in the cloud: Facebook hands the plain Apify runner IP and the shared datacenter pool a login wall and zero rows, while a US residential exit returns real listings. The country is pinned on purpose, because a geo-random exit would quietly return a different region's Marketplace rather than an error.

Will I get charged twice if the same listing shows up under two search terms? No. Listings are deduplicated across the whole run — a repeat is never pushed to the dataset or billed twice.

🙋 Your feedback

Found a query this doesn't handle right, or want a field we don't capture yet? Message DevilScrapes on Apify or open an issue from the Actor's Store page — we read every one and ship fixes fast.