Facebook Marketplace is the largest peer-to-peer classifieds platform on the internet, bigger than Craigslist, OfferUp, and eBay Classifieds combined. For resellers, price-intelligence teams, and brand-protection analysts, scraping it is high value — but Meta's anti-bot stack treats Marketplace as core infrastructure and defends it accordingly.
This guide is the practical 2026 playbook: what endpoints are reachable, what tokens you need, which proxy type keeps your account alive, and working code for listings, price history, and seller profiles.
What you cannot scrape: private messages, buyer/seller personal data beyond their public Facebook profile, listings that have been removed (Meta purges them fast from public endpoints), or anything from a region where you're not logged in via an IP in that region.
Individual Marketplace listings at facebook.com/marketplace/item/<id> partially render without login. Meta returns a trimmed HTML document with Open Graph tags (title, price, image) and a JSON blob inside a script tag. This is enough for basic price-monitoring and brand-protection tasks.
Limits: no seller detail, no description beyond a snippet, no message link, no photos beyond the hero image. Rate limits bite fast — around 30–60 requests per hour per IP.
facebook.com/api/graphql/)Once logged in, Marketplace uses a single GraphQL endpoint at /api/graphql/. Every query has a doc_id (the precompiled query hash), variables (what you're asking for), and a session-bound fb_dtsg CSRF token. Scraping here gives you the full data, including descriptions, photo URLs, seller details, and location to the kilometer.
This is the production path. The effort goes into session management — maintaining a pool of warm Facebook accounts, each behind a stable mobile or ISP proxy in the target country.
Apify, Bright Data, SerpApi, and several others sell Marketplace scraping as a service. Expect $2–$5 per 1,000 listings. If your volume is under ~50k listings/month, this is almost always cheaper than building a session pool.
Facebook is the most aggressive anti-scraping target in 2026, full stop. What works:
The lightest-weight path: pull the public listing page and extract the embedded data:
import httpx
import json
import re
PROXY = "http://username:[email protected]:8000"
def scrape_marketplace_listing(listing_id: str) -> dict:
url = f"https://www.facebook.com/marketplace/item/{listing_id}/"
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
with httpx.Client(proxies=PROXY, headers=headers, timeout=30) as client:
r = client.get(url, follow_redirects=True)
r.raise_for_status()
# Marketplace embeds listing JSON in a script block
m = re.search(
r'"marketplace_listing_title":"([^"]+)".*?'
r'"formatted_amount":"([^"]+)".*?'
r'"reverse_geocode_detailed":\{[^}]*"city":"([^"]*)"',
r.text,
re.DOTALL,
)
if not m:
raise RuntimeError("listing JSON not found — IP may be blocked")
title, price, city = m.groups()
return {
"id": listing_id,
"title": title.encode().decode("unicode_escape"),
"price": price,
"city": city,
}
print(scrape_marketplace_listing("1234567890123456"))
This is fragile — the regexes break whenever Meta reorders fields. For production, parse the __isScriptWithServerHeaders JSON blob properly. But the pattern is enough for price-check tasks.
For category browsing you need: a logged-in session cookie (c_user, xs, datr), the fb_dtsg CSRF token extracted from the page, and the current doc_id for the MarketplaceSearch query (changes every few weeks).
import httpx
PROXY = "http://username:[email protected]:8000"
COOKIES = {
"c_user": "YOUR_USER_ID",
"xs": "YOUR_XS_TOKEN",
"datr": "YOUR_DATR",
}
FB_DTSG = "EXTRACTED_FROM_PAGE_LOAD"
DOC_ID = "8134567890123456" # rotate — inspect network tab
def search_marketplace(query: str, city_id: str, radius_km: int = 65):
variables = {
"buyLocation": {"latitude": 0, "longitude": 0},
"count": 24,
"cursor": None,
"query": query,
"marketplaceBrowseContext": "CATEGORY_FEED",
"marketplaceCategoryID": "",
"locationID": city_id,
"radius": radius_km,
}
data = {
"av": COOKIES["c_user"],
"fb_dtsg": FB_DTSG,
"fb_api_caller_class": "RelayModern",
"doc_id": DOC_ID,
"variables": str(variables).replace("'", '"').replace("None", "null"),
}
with httpx.Client(
proxies=PROXY,
cookies=COOKIES,
headers={"User-Agent": "Mozilla/5.0 Chrome/125"},
timeout=30,
) as c:
r = c.post("https://www.facebook.com/api/graphql/", data=data)
r.raise_for_status()
j = r.json()
listings = (
j.get("data", {})
.get("marketplace_search", {})
.get("feed_units", {})
.get("edges", [])
)
return [
{
"id": n["node"]["listing"]["id"],
"title": n["node"]["listing"]["marketplace_listing_title"],
"price": n["node"]["listing"]["listing_price"]["formatted_amount"],
"city": n["node"]["listing"].get("location", {}).get("reverse_geocode", {}).get("city"),
}
for n in listings
if n.get("node", {}).get("listing")
]
Marketplace results are heavily geo-filtered. If you log in from a US IP and search "iPhone 15", you get US listings priced in USD. Same query from a UK IP returns UK listings in GBP. Meta determines this from both the IP geolocation and the account's saved location preference.
To scrape a specific city, pair a residential or ISP proxy in that city with an account whose saved location matches. SpyderProxy supports city-level targeting on Budget Residential and Static Residential pools, which is the critical feature.
Fresh Facebook accounts get checkpoint-challenged within hours if they immediately start scraping. The rule of thumb we see working in 2026:
Cost per warmed, scraping-ready account: ~$5–$15 including phone verification, LTE warmup, and the first week of ISP proxy time.
doc_id or stale fb_dtsg. Re-scrape the page to get fresh values.Publicly visible Marketplace listings have been treated as scrapable public data in recent US case law. Meta's Terms of Service prohibit automated access, which gives them grounds to ban accounts but is not a criminal matter. Do not scrape buyer/seller PII beyond what is publicly visible, do not circumvent login walls with stolen credentials, and do not aggregate personal data on EU residents without a GDPR lawful basis. When in doubt, ask counsel.
Yes, but only individual listing URLs — the public page returns enough HTML for basic title, price, and city. Category feeds and search require a logged-in session.
Static residential (ISP) proxies for authenticated scraping, because Facebook ties account trust to a stable IP. LTE mobile proxies for account creation and warming. Never use rotating residential IPs on a logged-in session.
One warmed account per 200–400 requests/hour you want to run. For 10k listings/day, budget 3–5 accounts in a rotation.
Yes, eventually. Never scrape from a personal account you care about. Use purpose-built, separately-warmed scraping accounts, each pinned to its own ISP proxy.
Two things must match: the proxy IP must geolocate to that city (or nearby), and the scraping account's saved location must be set there. Use a SpyderProxy residential or ISP IP with city-level targeting.
It's the hash Facebook uses to identify a precompiled query. Open Marketplace in Chrome DevTools, switch to the Network tab, filter for "graphql", and copy the doc_id from a MarketplaceSearch request. It rotates every few weeks.
No — these are not exposed publicly. Messaging happens through Facebook's own chat. Attempting to exfiltrate contact info likely violates both Meta's ToS and data-protection law in most jurisdictions.
Re-scrape the listing URL on a schedule (e.g. every 6 hours) and diff the price field. Marketplace preserves the same URL across price edits, so re-scraping is the standard approach.
Scraping Facebook Marketplace in 2026 requires investment: warmed accounts, ISP proxies, a stable session architecture, and a tolerance for periodic doc_id and DTSG rotation. For low volume, buy a commercial scraping API. For serious volume, build the session pool and pair each account with SpyderProxy Static Residential ($3.90/day) for the account's lifetime. Start new accounts from LTE Mobile proxies during the warmup week.