Facebook Marketplace Scraper - Listings, Prices, Sold Flag
Pricing
from $0.40 / 1,000 listings
Facebook Marketplace Scraper - Listings, Prices, Sold Flag
Search Facebook Marketplace by keyword and city. One row per listing: title, price, any price cut, town, link, photo, posted date and sold status. Narrow it by price range and days listed, or change the sort order. About 24 listings a search. No login needed from you. $0.40 per 1,000 listings.
Pricing
from $0.40 / 1,000 listings
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
17
Total users
8
Monthly active users
10 hours ago
Last modified
Categories
Share
Facebook Marketplace Scraper: listings and prices for a search, in the city you pick
Tell it what you're after and where, and it returns the Facebook Marketplace listings that search turns up, one row each. Every row has the title, the price and any crossed-out price, the seller's town, the listing link, the main photo and how many days the item has been up. The limit to know before you plan a big job: one search returns about 24 listings, Facebook's first page of results, however high you set maxItems.
Two more things. Five filters on the form (condition, category, sold only, exact phrase and radius) don't change the results at the moment, so don't count on them. And the form arrives filled in with a real search, road bikes in Austin, so pressing Start without editing it runs that search and bills what it finds.
| Input | Search terms and a place, or Marketplace search links from your browser |
| Output | One row per listing |
| Ceiling | About 24 listings per search, up to 10 searches per run |
| Account needed | None from you |
| Price | $0.40 per 1,000 listings, flat on every plan |
๐ What Facebook Marketplace Scraper does
It runs the search you would run on Marketplace yourself, in the place you name, and writes down what Facebook shows. That is Facebook's idea of the area, not a fixed circle. A search for road bikes in Austin also brought back listings from Round Rock, Georgetown and San Antonio.
Up to ten search terms can share a run. The row budget is split evenly between them, and a listing that two of your terms both find comes back once and is charged once.
A place name Facebook doesn't recognise gets quietly answered with listings from some other city. When Facebook's answer says it searched somewhere other than the place you asked for, those listings are thrown away and you get a free WRONG_LOCATION row instead of a bill for the wrong town.
Already built the search in your browser? Paste the link into startUrls and the term, city, price range, day limit and sort order are read out of it. Each link is searched in its own city, so one run can cover several.
๐ฅ What you give it
{"searchTerms": ["road bike"],"location": "Austin, TX","minPrice": 300,"maxPrice": 600,"daysSinceListed": 7,"maxItems": 24}
| Field | If you leave it empty | What it is |
|---|---|---|
searchTerms | your links only, or the free sample row | What to look for, up to 10 terms. The form arrives with road bike in it. |
location | the city in your first link, if any | Where your search terms are searched: a city name ("Austin, TX", "Manchester"), a Marketplace city slug ("nyc", "la") or a latitude,longitude pair. Marketplace search is local, so a run needs a place. Links keep the city they name. |
maxItems | 20 | Total listings across all terms, 1 to 1,000, shared evenly between them. Each search still stops at about 24. |
minPrice, maxPrice | no limit | Price range in the currency of the place you search. |
daysSinceListed | any age | Only listings posted in the last 1 to 30 days. Facebook counts whole days, so a row's postedAt can be a little older than that. |
sortBy | Facebook's own order | best_match, newest, price_low_to_high, price_high_to_low or distance. |
startUrls | none | Up to 10 Marketplace search links, such as https://www.facebook.com/marketplace/nyc/search?query=road%20bike&minPrice=100. Each link is searched in the city it names. One whose city the run can't place (a numeric Facebook location id, for example) falls back to location, with a free NO_LOCATION row. A filter you also set in the fields above wins over the one in the link. |
latitude, longitude | none | Exact coordinates instead of a place name. Set both or neither. |
condition, category, availability, exactMatch, radiusKm | no effect | On the form, but the search currently runs as if they were empty. |
proxyUrls | empty | Only if you want the requests to go out through servers of your own, as http://user:pass@host:port. |
sessionCookies | empty | Your own Facebook cookies, one account per line, as c_user=โฆ; xs=โฆ. Optional, see the questions below. |
๐ค What you get back
A real row from a real run, with the photo link cut short:
{"ok": true,"charged": true,"recordType": "listing","searchTerm": "road bike","listingId": "1807545393723285","listingUrl": "https://www.facebook.com/marketplace/item/1807545393723285/","title": "Giant TCR C3 Road Bike. 600$ OBO. Full Carbon Frame + Fork. 700x 23c tires.","price": 600,"priceFormatted": "$600","currency": "USD","originalPrice": 850,"priceMin": null,"priceMax": null,"city": "Austin","state": "TX","locationName": "Austin, Texas","imageUrl": "https://scontent.fpei1-1.fna.fbcdn.net/v/t39.84726-6/818962247_1436233761733825_8678125269974170042_n.jpg?stp=...","videoUrl": null,"postedAt": "2026-09-20T15:32:49.000Z","postedAtTimestamp": 1789918369,"daysListed": 9,"isSold": false,"isPending": false,"isLive": true,"categoryId": "1658310421102081","deliveryTypes": ["IN_PERSON"],"subtitle": null,"fromGroup": null,"scrapedAt": "2026-09-30T02:26:04.593Z"}
| Field | What it is |
|---|---|
listingId, listingUrl | The item's id and permanent link. Stable, so use either as your key across runs. |
price, priceFormatted, currency | The asking price as a number, the exact text Facebook shows, and the currency code worked out from that text's symbol. price is null when the listing has none. |
originalPrice | The crossed-out price when the seller has cut it, otherwise null. The row above dropped from 850 to 600. |
priceMin, priceMax | Filled in only for listings priced as a range. |
city, state, locationName | Where the seller says the item is. state is empty in countries without one. |
postedAt, postedAtTimestamp, daysListed | When it was listed, as an ISO date and as epoch seconds, and how many days ago that was. |
isSold, isPending, isLive | The listing's status when the run read it. A search shows what is for sale now, so isSold is false on nearly every row. |
deliveryTypes | How the seller will hand it over, such as IN_PERSON, PUBLIC_MEETUP or DOOR_PICKUP. |
categoryId | Facebook's internal department id. Fine for grouping, but Facebook publishes no name for it, so none is made up here. |
subtitle, fromGroup | The extra line on vehicle listings (mileage and so on), and the group name when the item was cross-posted from one. Null on most rows. |
searchTerm | Which of your terms found it. |
๐งพ Reading the output
Three kinds of row can land in the dataset.
| Row | How to spot it | Charged |
|---|---|---|
| A listing | charged: true | yes |
| The sample row | _sample: true | no |
| A diagnostic | _diagnostic: true, with an errorCode and a details sentence | no |
The sample row only appears when the input has no search terms and no links. It shows the shape of a real row and also says recordType: "listing", so filter on charged rather than on recordType to keep just the listings.
errorCode | What it means |
|---|---|
NO_LOCATION | The run had no place it could search, so nothing was searched. On a link's row it means that link's city couldn't be placed, so the run's own location was searched instead. |
WRONG_LOCATION | Facebook didn't recognise the place and answered with somewhere else. Those listings were thrown away. |
NO_RESULTS | Nothing for that term in that place. A narrow term, a tight price range, or a sort order used together with daysSinceListed can do it. |
BLOCKED | Facebook wouldn't show that search this time. Running it again later usually works. |
BAD_INPUT | A link in startUrls isn't a Marketplace link. |
NETWORK, TIME_BUDGET, CHARGE_ERROR and the rest | Rare. The details sentence says what happened. |
โถ๏ธ How to run it
- Open Facebook Marketplace Scraper and click Try for free.
- Type what you're looking for into Search terms, one per line.
- Put the city in Location.
- Set Maximum rows. Under Filters, add a price range or Listed within the last N days if you want them.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it from the API.
๐ฐ How much does it cost?
$0.40 per 1,000 listings, flat on every Apify plan.
You pay per listing row. The sample row and the diagnostic rows are never charged. A listing that two of your terms both found is charged once, and one thrown away because Facebook searched the wrong city isn't charged at all.
๐ก What people use it for
- Resellers running the same few terms every morning with
daysSinceListedset to 1, so each run holds only what went up since yesterday. - Pricing something before buying or listing it: take the median of
priceacross two dozen local listings instead of guessing. - Finding sellers who have already come down. Every row with an
originalPriceis one of them. - Keeping a spreadsheet of what's for sale in a category you trade in, instead of scrolling the feed by hand.
๐ง What it does not do
- More than about 24 listings per search. That is Facebook's first page, and there is no paging past it. A bigger
maxItemsonly helps if you add more terms. - Condition, category, sold only, exact phrase or radius filtering. Those fields are on the form but change nothing right now.
- Seller names or item condition as columns. Facebook leaves both out of search results.
- Shipped items only. Asked for those, Facebook's search here returns nothing.
- Several cities for your typed search terms.
locationtakes one place. To cover more cities in one run, paste a Marketplace link for each city intostartUrls. - A radius you choose. Facebook decides how wide to look, and an Austin search can pull in towns more than 100 km away.
- Listings only group members can see. You get what Facebook shows anyone.
- Photo links that last.
imageUrlpoints at Facebook's image server and stops working after a while, so download the photos you want to keep.
๐งญ Which marketplace scraper do you need?
| If you want | Use |
|---|---|
| Facebook Marketplace listings for a search, in the cities you name | This one |
| Classified ads from one Craigslist city and category | Craigslist Scraper |
| Classified ads on Gumtree in the UK | Gumtree UK Scraper |
| What an item actually sold for on eBay | eBay Sold Listings Scraper |
| Posts from a public Facebook group, buy-and-sell groups included | Facebook Groups Scraper |
โ Questions people ask
Do I need a Facebook account? No. Nothing of yours is used unless you paste cookies into sessionCookies. Do that only if you want the run to use your own account and rate limit instead of the one the actor shares between everyone who runs it. Treat those values like a password: anyone holding them can act as you, and Facebook may sign the session out.
Why do I have to give a place? Marketplace only searches locally. With no place the run writes a free NO_LOCATION row and stops before searching anything.
Why did I get fewer rows than maxItems? Each search stops at about 24, the budget is split evenly between your terms, and a listing found twice counts once.
Why did a search come back empty? Look at the diagnostic row. NO_RESULTS means the filters were too tight for that place, WRONG_LOCATION means try the plain city name or the short name Facebook uses in its own links, and BLOCKED means try again later.
Can I run it every day? Yes. Nothing is kept between runs, so use listingId to spot what's new and postedAt to sort it.
Is scraping Marketplace legal? The listings are public, though photos and descriptions sometimes carry personal details, so keep what you need and think before republishing it. Apify's article on whether web scraping is legal is worth reading first. None of this is legal advice.
๐ If something breaks
Open the Issues tab on the actor page and send the input you used with the run ID. The diagnostic rows in the dataset usually name the reason already.