     [Blog](https://scrapfly.io/blog)   /  [playwright](https://scrapfly.io/blog/tag/playwright)   /  [How to Scrape Facebook Marketplace and Events With Python](https://scrapfly.io/blog/posts/how-to-scrape-facebook)   # How to Scrape Facebook Marketplace and Events With Python

 by [Ziad Shamndy](https://scrapfly.io/blog/author/ziad) May 29, 2026 11 min read [\#playwright](https://scrapfly.io/blog/tag/playwright) [\#python](https://scrapfly.io/blog/tag/python) [\#scrapeguide](https://scrapfly.io/blog/tag/scrapeguide) 

 [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook "Share on LinkedIn")    

 

 

         

Facebook Marketplace and Events are rich public data sources, but a plain HTTP request cannot reach them. A login modal blocks the DOM before listings load. Facebook then adds JavaScript challenges, behavior checks, and IP reputation filters. Scraping Facebook in 2026 comes down to three jobs: close the modal and survive the bot layer. Then parse the JSON Facebook ships to its UI inside `<script>` tags.

In this guide, we'll use [Scrapfly's API](https://scrapfly.io/web-scraping-api). It closes the login modal as a browser scenario. We'll route through residential IPs and parse Marketplace listings and Event cards from embedded JSON payloads. Let's get started.

[**Latest Facebook Scraper Code**github.com/scrapfly/scrapfly-scrapers/facebook-scraper](https://github.com/scrapfly/scrapfly-scrapers/tree/main/facebook-scraper)



## Key Takeaways

- The login modal that blocks Marketplace and Events is dismissable. It is not an authentication wall. A single click on `div[aria-label="Close"]` exposes the public listing data without logging in.
- The stable extraction target on Facebook is JSON inside `<script type="application/json">` tags. Do not rely on the DOM. Marketplace listings live as `MarketplaceProductItem` and `GroupCommerceProductItem` objects. Events live as `Event` objects.
- Facebook flags repeated requests by TLS fingerprint and behavior pattern before IP. Rotating User-Agents alone will not extend a session. CSS classes like `x8gbvx8 x78zum5` rotate weekly. Never use them as your main hook.
- A `js_scenario` can wait for the modal, click Close, wait again, then scroll to bottom. That is the smallest useful shape for a live Facebook scraper.
- For steady Marketplace and Events scraping, [Scrapfly's Web Scraping API](https://scrapfly.io/web-scraping-api) runs the modal click and scroll as a `js_scenario`. It routes through residential IPs and returns rendered HTML. Then one `find_objects` walker works against either surface.

**Get web scraping tips in your inbox**Trusted by 100K+ developers and 30K+ enterprises. Unsubscribe anytime.







## Why Is Facebook Hard to Scrape?

Facebook does not block scraping at one layer. It stacks three separate defenses on every public page. Any one of them can break a naive scraper.

The first defense is the login modal. Marketplace and Events both render content behind a full-screen overlay. It asks you to sign in. The data is in the DOM, but the modal traps clicks and scrolling. A scraper that does not dismiss the modal sees the listings briefly. Then it loses them as the page settles into its blocked state.

The second defense is the anti-bot stack. Facebook checks your TLS handshake, HTTP/2 SETTINGS frame, and browser fingerprint before it serves the page. A plain `requests.get()` gets a redirect to the login wall. A headless Chromium with default settings gets flagged on the first navigation. Rotating User-Agents does nothing because Facebook catches the fingerprint mismatch first.

The third defense is behavior analysis. Even a real browser can hit rate limits if it scrapes too fast from one IP. Facebook tracks scroll patterns, mouse movements, and timing across requests. Residential IPs help, but only when the request rate looks human.

[How to Scrape Hidden APIsIn this tutorial we'll be taking a look at scraping hidden APIs which are becoming more and more common in modern dynamic websites - what's the best way to scrape them?](https://scrapfly.io/blog/posts/how-to-scrape-hidden-apis)



## How to Set Up Scrapfly for Facebook

The whole setup runs through the Scrapfly Python SDK. Install it first. Then configure a base request profile for the login modal, residential IPs, and JavaScript rendering.

bash```bash
pip install scrapfly-sdk
```



The SDK wraps the Scrapfly Web Scraping API and exposes the `ScrapflyClient` and `ScrapeConfig` you need to send requests. Get an API key from the [Scrapfly dashboard](https://scrapfly.io/dashboard) and export it as `SCRAPFLY_KEY`.

python```python
import os
import re
import json
from urllib.parse import quote
from scrapfly import ScrapeConfig, ScrapflyClient

SCRAPFLY = ScrapflyClient(key=os.environ["SCRAPFLY_KEY"])

JS = [
    {"wait_for_selector": {"selector": "div[aria-label='Close']", "timeout": 3000}},
    {"click": {"selector": "div[aria-label='Close']"}},
    {"wait": 500},
    {"scroll": {"selector": "bottom"}},
]
BASE_CONFIG = {
    "asp": True,
    "country": "US",
    "render_js": True,
    "js_scenario": JS,
    "proxy_pool": "public_residential_pool",
}
```



The `js_scenario` array is the key piece for Facebook. It waits up to three seconds for the Close button on the login modal. Then it clicks the button, waits half a second, and scrolls to the bottom so more listings hydrate. Scrapfly returns the HTML after the page settles. `asp=True` handles the TLS, fingerprint, and behavior checks. `proxy_pool="public_residential_pool"` keeps every request on a residential IP that Facebook treats as a real visitor. Every snippet below reuses this `BASE_CONFIG`.



Scrapfly

#### Need a cloud browser for scraping?

Run headless browsers at scale with Scrapfly Cloud Browser — no infrastructure to manage.

[Try Free →](https://scrapfly.io/register)## How Do You Scrape Facebook Marketplace?

Facebook Marketplace ships its listings as JSON inside `<script type="application/json">` tags, not the rendered DOM. The visible cards use CSS classes that rotate between deploys, but the underlying JSON objects carry a stable `__typename` of `MarketplaceProductItem` or `GroupCommerceProductItem`. Walk the JSON tree for those typenames and you get a clean listing array that survives Facebook's UI changes.

python```python
def find_objects(obj, typename, depth=0):
    """Recursively find all objects with a given __typename in Facebook's JSON tree."""
    if depth > 50:
        return []
    if isinstance(obj, dict):
        results = [obj] if obj.get("__typename") == typename else []
        return results + [item for v in obj.values() for item in find_objects(v, typename, depth + 1)]
    if isinstance(obj, list):
        return [item for v in obj for item in find_objects(v, typename, depth + 1)]
    return []


def parse_marketplace_listing(result):
    """Extract Marketplace listings from script tags in the rendered HTML."""
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>',
        result.content,
        re.DOTALL,
    )
    listings = []
    for script in scripts:
        try:
            data = json.loads(script)
        except json.JSONDecodeError:
            continue
        listings.extend(find_objects(data, "MarketplaceProductItem"))
        listings.extend(find_objects(data, "GroupCommerceProductItem"))

    parsed = []
    for item in listings:
        geocode = item.get("location", {}).get("reverse_geocode", {})
        city, state = geocode.get("city", ""), geocode.get("state", "")
        location = f"{city}, {state}" if city and state else (city or state)
        seller = item.get("marketplace_listing_seller") or {}
        image = item.get("primary_listing_photo", {}).get("image") or {}

        parsed.append({
            "id": item.get("id"),
            "title": item.get("marketplace_listing_title"),
            "price": item.get("formatted_price", {}).get("text"),
            "location": location,
            "is_sold": item.get("is_sold", False),
            "creation_time": item.get("creation_time"),
            "seller": {"name": seller.get("name"), "id": seller.get("id")} if seller else None,
            "image_url": image.get("uri"),
            "category_id": item.get("marketplace_listing_category_id"),
        })
    return parsed


async def scrape_marketplace_listings(query: str = "electronics"):
    """Fetch a Marketplace search page through Scrapfly and parse it."""
    url = f"https://www.facebook.com/marketplace/search/?query={quote(query)}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url, **BASE_CONFIG))
    return parse_marketplace_listing(result)
```



The `find_objects` walker recurses through every nested dict and list in the JSON payload. It picks out objects by their `__typename` field. The parser pulls the most useful keys: listing ID, title, formatted price, location, sold flag, creation time, seller, primary image, and category ID. Add fields like `delivery_types` or `is_pending` the same way if your pipeline needs them.

 Example Outputjson```json

COUNT=15
{
  "id": "1496458995600560",
  "title": "Electronics ⚡️🔌💡",
  "price": null,
  "is_sold": false,
  "creation_time": null
}
{
  "id": "3186910231492394",
  "title": "Lot of 6 Electronics (Phones & Tablets)",
  "price": null,
  "is_sold": false,
  "creation_time": null
}
{
  "id": "853859730610824",
  "title": "Free phones",
  "price": null,
  "is_sold": false,
  "creation_time": null
}
  
```



Marketplace search results expose listing IDs and titles, but several fields like `price` and `creation_time` come back as null. Facebook only populates those on the individual detail pages. To get pricing per listing, follow the listing URL at `https://www.facebook.com/marketplace/item/{id}/` and run the same `find_objects` walker against the detail page's script tags. The detail HTML carries the full `formatted_price`, `creation_time`, and seller block.



## How Do You Scrape Facebook Events?

Facebook Events use the same JSON-in-script-tag pattern as Marketplace. The objects you want carry `__typename: "Event"` and live on the Events search URL. Everything else stays the same: residential proxy, modal-dismissal scenario, and `find_objects` walker.

python```python
def parse_event(result):
    """Extract Event objects from script tags in the rendered HTML."""
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>',
        result.content,
        re.DOTALL,
    )
    events = []
    for script in scripts:
        try:
            data = json.loads(script)
        except json.JSONDecodeError:
            continue
        events.extend(find_objects(data, "Event"))

    parsed = []
    for event in events:
        place = event.get("event_place") or {}
        location = place.get("contextual_name") or (
            "Online Event" if event.get("is_online") else ""
        )
        photo = event.get("cover_photo", {}).get("photo") or {}

        parsed.append({
            "id": event.get("id"),
            "title": event.get("name"),
            "date": event.get("day_time_sentence"),
            "location": location,
            "url": event.get("url") or event.get("eventUrl"),
            "start_timestamp": event.get("start_timestamp"),
            "is_online": event.get("is_online", False),
            "is_past": event.get("is_past", False),
            "cover_photo": photo.get("eventImage", {}).get("uri"),
            "social_context": (event.get("social_context") or {}).get("text"),
        })
    return parsed


async def scrape_facebook_events(query: str = "tech meetup"):
    """Fetch a Facebook Events search page through Scrapfly and parse it."""
    url = f"https://www.facebook.com/events/search?q={quote(query)}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url, **BASE_CONFIG))
    return parse_event(result)
```



The Event parser pulls the title, the human-readable `day_time_sentence`, and the location or `Online Event` fallback. It also extracts the canonical URL, start timestamp, cover photo, and `social_context`. Facebook uses `social_context` for engagement text, such as `"79 interested · 19 going"`. Past events come through with `is_past: true`. Use that flag to filter them out when your pipeline only needs upcoming dates.

The same walker pattern extends to any other public surface on Facebook. Swap the `__typename` and the search URL, and the rest of the pipeline keeps working: Pages, Groups, and Reels all ship their data the same way.

## Powering Facebook Scraping with Scrapfly



ScrapFly's [Web Scraping API](https://scrapfly.io/web-scraping-api) is a single HTTP endpoint for collecting web data at scale, with a **99.99% success rate** across **130M+ proxies in 120+ countries**.

- [Anti-Scraping Protection bypass](https://scrapfly.io/docs/scrape-api/anti-scraping-protection) - automatically defeats Cloudflare, DataDome, PerimeterX, Akamai, and 90+ other bot systems.
- [Smart proxy rotation](https://scrapfly.io/docs/scrape-api/proxy) - residential and datacenter pools with country and ASN level geo-targeting.
- [JavaScript rendering](https://scrapfly.io/docs/scrape-api/javascript-rendering) - render SPAs and dynamic pages through real cloud browsers.
- [Browser automation scenarios](https://scrapfly.io/docs/scrape-api/javascript-scenario) - scroll, click, fill forms, and wait for elements without managing a browser fleet.
- [Format conversion](https://scrapfly.io/docs/scrape-api/getting-started#api_param_format) - return pages as HTML, JSON, clean text, or LLM ready Markdown.
- [Session management](https://scrapfly.io/docs/scrape-api/session) - keep cookies, headers, and IPs consistent across multi step flows.
- [Smart caching](https://scrapfly.io/docs/scrape-api/getting-started#api_param_cache) - cache successful responses to cut cost on repeat scraping jobs.
- [Python](https://scrapfly.io/docs/sdk/python), [TypeScript](https://scrapfly.io/docs/sdk/typescript), [Scrapy](https://scrapfly.io/docs/sdk/scrapy), and [no-code integrations](https://scrapfly.io/docs/integration/getting-started) including Make, n8n, Zapier, LangChain, and LlamaIndex.



## FAQ

Do you need a Facebook account to scrape Marketplace and Events?No. Both surfaces serve as listings of public data behind a modal you can dismiss. A scraper that closes the modal can read the same JSON payloads without logging in.







Why are some Marketplace fields null on the search page?Facebook only ships the full price, creation time, and seller block on the individual `marketplace/item/{id}/` detail pages. Follow the listing URL and run the same parser against the detail HTML to fill those fields in.







Is it legal to scrape Facebook Marketplace and Events?Scraping public Facebook data is generally legal in most jurisdictions, but Facebook's Terms of Service prohibit automated access. Check the legal disclaimer at the bottom of this article and consult counsel for your specific use case.







Why do my Facebook scrapes start working then suddenly fail?Facebook flags repeated requests by TLS fingerprint and behavior pattern, so a session that worked for ten requests can fail on the eleventh. Using `asp=True` with a residential proxy pool resets the fingerprint and IP on each request.









## Summary

Scraping Facebook comes down to three things: dismiss the login modal, pass the anti-bot stack, and parse JSON from script tags. Marketplace listings live as `MarketplaceProductItem` and `GroupCommerceProductItem` objects. Events live as `Event` objects, and the same `find_objects` walker handles both with a single typename swap.

Scrapfly's `js_scenario` handles the modal click and scroll. `asp=True` handles fingerprint and behavior checks. `proxy_pool="public_residential_pool"` keeps every request on a residential IP. The `parse_marketplace_listing` and `parse_event` functions then fit into a concurrent crawl loop. You do not need a browser fleet, CSS selector repairs after every deploy, or in-house proxy rotation logic.



Legal Disclaimer and PrecautionsThis tutorial covers popular web scraping techniques for education. Interacting with public servers requires diligence and respect:

- Do not scrape at rates that could damage the website.
- Do not scrape data that's not available publicly.
- Do not store PII of EU citizens protected by GDPR.
- Do not repurpose *entire* public datasets which can be illegal in some countries.

Scrapfly does not offer legal advice but these are good general rules to follow. For more you should consult a lawyer.

 

   Table of Contents















 

  Table of Contents- [Key Takeaways](#key-takeaways)
- [Why Is Facebook Hard to Scrape?](#why-is-facebook-hard-to-scrape)
- [How to Set Up Scrapfly for Facebook](#how-to-set-up-scrapfly-for-facebook)
- [How Do You Scrape Facebook Marketplace?](#how-do-you-scrape-facebook-marketplace)
- [How Do You Scrape Facebook Events?](#how-do-you-scrape-facebook-events)
- [Powering Facebook Scraping with Scrapfly](#powering-facebook-scraping-with-scrapfly)
- [FAQ](#faq)
- [Summary](#summary)
 
    Join the Newsletter  Get monthly web scraping insights 

 

  



Scale Your Web Scraping

Anti-bot bypass, browser rendering, and rotating proxies, all in one API. Start with 1,000 free credits.

  No credit card required  1,000 free API credits  Anti-bot bypass included 

 [Start Free](https://scrapfly.io/register) [View Docs](https://scrapfly.io/docs/onboarding) 

 Not ready? Get our newsletter instead. 

 

## Explore this Article with AI

 [ ChatGPT ](https://chat.openai.com/?q=Summarize%20this%20page%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [ Gemini ](https://www.google.com/search?udm=50&aep=11&q=Summarize%20this%20page%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [ Grok ](https://x.com/i/grok?text=Summarize%20this%20page%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [ Perplexity ](https://www.perplexity.ai/search/new?q=Summarize%20this%20page%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [ Claude ](https://claude.ai/new?q=Summarize%20this%20page%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) 



 ## Related Articles

 [  

 python scrapeguide 

### How to Scrape Reddit Posts, Subreddits and Profiles

In this article, we'll explore how to scrape Reddit. We'll extract various social data types from subreddits, posts, and...

 

 ](https://scrapfly.io/blog/posts/how-to-scrape-reddit-social-data) [  

 blocking 

### How to Bypass Cloudflare When Web Scraping in 2026

Cloudflare offers one of the most popular anti scraping service, so in this article we'll take a look how it works and h...

 

 ](https://scrapfly.io/blog/posts/how-to-bypass-cloudflare-anti-scraping) [  

 python scrapeguide 

### How to Scrape YellowPages.com in 2026

Tutorial on how to scrape yellowpages.com business and review data using Python. How to avoid blocking to scrape data at...

 

 ](https://scrapfly.io/blog/posts/how-to-scrape-yellowpages) 

  ## Related Questions

- [ Q Mobile vs Residential Proxies - which to choose for scraping? ](https://scrapfly.io/blog/answers/mobile-vs-residential-proxies-whats-the-difference)
 
  



   



 Run headless browsers at scale, **1,000 free credits** [Start Free](https://scrapfly.io/register)