     [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) Jul 12, 2026 18 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") [  ](https://x.com/intent/tweet?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook&text=How%20to%20Scrape%20Facebook%20Marketplace%20and%20Events%20With%20Python "Share on X") [  ](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook "Share on Facebook")    

 

 

Summarize this article with

 [  ](https://chat.openai.com/?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [  ](https://claude.ai/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [  ](https://x.com/i/grok?text=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [  ](https://www.perplexity.ai/search/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) [  ](https://www.google.com/search?udm=50&aep=11&q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-facebook) 



         

You want to track public Facebook data. Marketplace deals, event schedules, a competitor's page. Meta's Graph API covers almost none of it, and a plain `requests.get()` bounces off a login wall before any listing loads.

Facebook still serves most of this data to logged-out browsers, buried in JSON inside its own `<script>` tags. This guide covers six public surfaces: Pages, Posts, Profiles, Marketplace, Events, and Groups. You scrape them with the Scrapfly SDK and one extraction pattern that ignores Facebook's rotating CSS classes.



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

## Key Takeaways

Facebook hands its public data to logged-out browsers as JSON inside `<script>` tags. One extraction pattern reads six surfaces from it, with no login and no Graph API.

- **Six public surfaces, one pattern**: Pages, Posts, Profiles, Marketplace, Events, Groups
- **Durable technique**: match `__typename` in embedded JSON, not rotating CSS classes
- **Marketplace and Events ship as tested code**; the other four use the same approach
- **Public data only**: no login, no cookies, no Graph API app review
- **Scrapfly clears the modal and anti-bot layer**, so your code only parses JSON

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







## Why Scrape Facebook?

Facebook is one of the largest public directories of businesses, events, and secondhand goods online. Most scraping work comes down to three jobs, and all three need public data the official API won't hand over.

The first is brand monitoring. Public Pages and their posts hold reviews, complaints, and engagement counts that feed sentiment tracking. Facebook's anti-bot systems are among the most aggressive on the web, so this data is hard to collect at scale without help.

The second is lead generation and B2B research. Public business Pages expose names, categories, follower counts, and contact links that feed prospecting pipelines.

The third is deal alerts and market research. Marketplace is a live feed of local supply and pricing. As r/learnpython users regularly point out, this data is public without any login. That makes it a natural target for price tracking and regional supply intel.

All three need reliable extraction that survives Facebook's changes and its blocks. Let's compare the ways to get there.



## How to Scrape Facebook: Methods at a Glance

Three approaches exist, and only one covers the public surfaces this guide targets without constant repair.

Meta's official APIs (Graph API, Meta Content Library) return structured data but sit behind app review. They cover a fraction of public data and change their schema often.

Open-source libraries like [kevinzg/facebook-scraper](https://github.com/kevinzg/facebook-scraper) were strong in 2020 to 2022 but break more often each year. The Scrapfly SDK with JSON extraction routes through residential proxies, renders JavaScript, and reads the JSON Facebook embeds in its own pages.

| Method | Login required | Survives FB changes | Production ready |
|---|---|---|---|
| Graph API | Yes (app review) | Stable for what it covers | Yes, but narrow scope |
| kevinzg/facebook-scraper | Optional cookies | No (breaks often) | No |
| Scrapfly SDK + JSON extraction | No | Yes (JSON schema stable) | Yes |

The rest of this guide builds the third row. Setup comes first.



## Project Setup

This guide covers public Facebook surfaces only, the data Facebook serves to logged-out browsers. It does not cover login-walled content, private groups, or anything that needs Meta API app review. For app-licensed access to non-public data, use Meta's Graph API or Meta Content Library.

You need Python 3.10 or newer and two packages: the Scrapfly SDK and loguru for readable logs. No Playwright, no Chromium.

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



Get an API key from the [Scrapfly dashboard](https://scrapfly.io/dashboard) and export it as `SCRAPFLY_KEY`. The canonical scraper lives in the [scrapfly-scrapers repo](https://github.com/scrapfly/scrapfly-scrapers/tree/main/facebook-scraper).

python```python
import re, os, json
from typing import Dict, List
from urllib.parse import quote
from loguru import logger as log
from scrapfly import ScrapeConfig, ScrapflyClient, ScrapeApiResponse

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",
}
```



Every request below reuses `BASE_CONFIG`. `asp=True` handles the TLS, fingerprint, and behavior checks, and `country="US"` pins a US viewer so content matches.

`render_js=True` waits for Facebook's client-side hydration, which is what populates the embedded JSON. `proxy_pool="public_residential_pool"` keeps every request on a residential IP.

The `js_scenario` needs one clarification, because it matters legally. Facebook injects a promotional sign-in overlay on public surfaces to push logged-out users toward registration.

That overlay is a marketing modal, not a security gate. The data underneath is public to any logged-out browser. The scenario closes it the same way a person clicks the X, then scrolls to load more items. It does not bypass authentication or reach any login-protected surface.



## The Core Pattern: Extracting JSON from Facebook's Embedded Scripts

Facebook renders its structured data into `<script type="application/json">` tags during server-side rendering. The visible cards use CSS classes like `x8gbvx8 x78zum5` that rotate between deploys, so selectors built on them break within weeks. The JSON is the stable target.

Every object in that JSON carries a `__typename` discriminator. One recursive walker collects every object matching the typenames you want. Each surface section below calls it with a different typename.

python```python
def find_objects_by_typename(obj, typenames, depth=0):
    """Recursively collect every object whose __typename matches one of `typenames`."""
    if depth > 50:
        return []
    if isinstance(obj, dict):
        results = [obj] if obj.get("__typename") in typenames else []
        return results + [
            item for value in obj.values()
            for item in find_objects_by_typename(value, typenames, depth + 1)
        ]
    if isinstance(obj, list):
        return [
            item for value in obj
            for item in find_objects_by_typename(value, typenames, depth + 1)
        ]
    return []
```



The `depth` guard stops runaway recursion on Facebook's deeply nested payloads. With the walker in place, each surface is a short function that fetches a URL and matches a typename.

[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 Scrape Public Facebook Pages and Profiles

Public business Pages and public profiles expose metadata to logged-out browsers: name, category, follower count, an about section, and contact links. Profiles behind friend-only privacy settings are out of scope; this covers only what Facebook shows without login.

Public Pages live at `https://www.facebook.com/{page_slug}` and public profiles at `https://www.facebook.com/profile.php?id={user_id}`. Facebook models both as `User` objects internally, so both render JSON with a `User` typename.

python```python
async def scrape_facebook_page(slug: str) -> List[Dict]:
    """Scrape a public Facebook Page or profile's metadata objects."""
    # Note: the canonical scrapfly-scrapers/facebook-scraper does not yet ship a
    # maintained Pages function. This follows the same pattern as Marketplace and
    # Events; expect a maintained version in a future release.
    url = f"https://www.facebook.com/{slug}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url=url, **BASE_CONFIG))
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>', result.content, re.DOTALL
    )
    pages = []
    for script in scripts:
        try:
            pages.extend(find_objects_by_typename(json.loads(script), ["User"]))
        except json.JSONDecodeError:
            continue
    return pages
```



Expect fields including `name`, `category`, `followers_count`, `about`, and `url` on the matched objects. Pull the ones your pipeline needs the same way the Marketplace parser does below.



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 to Scrape Public Posts from a Facebook Page

Public posts from a Page or public profile carry text, timestamps, engagement counts, and media URLs. Only logged-out-visible posts are in scope; follower-restricted posts are not.

Page post lists live at `https://www.facebook.com/{page_or_user}/posts`. Posts render as `Story` objects with `message`, `creation_time`, `attachments`, and a linked `Feedback` object holding likes, comments, and shares.

python```python
async def scrape_facebook_posts(page_slug: str) -> List[Dict]:
    """Scrape public posts from a Facebook Page or public profile."""
    # Note: no maintained Posts function ships in the canonical scraper yet.
    # The extraction pattern is identical to Marketplace and Events.
    url = f"https://www.facebook.com/{page_slug}/posts"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url=url, **BASE_CONFIG))
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>', result.content, re.DOTALL
    )
    posts = []
    for script in scripts:
        try:
            posts.extend(find_objects_by_typename(json.loads(script), ["Story"]))
        except json.JSONDecodeError:
            continue
    return posts
```



The `js_scenario` already scrolls to the bottom once, which loads the first batch of posts. Deep pagination needs several sequential calls with more scroll steps, so treat it as a follow-up exercise rather than a one-liner.



## How to Scrape Facebook Marketplace

Marketplace is fully backed by the canonical scraper. Its public search endpoint is `https://www.facebook.com/marketplace/search/?query={query}`, which returns listings without login. The promotional sign-in modal that overlays the results is closed by the `js_scenario` step from setup.

python```python
async def scrape_marketplace_listings(query: str = "electronics") -> List[Dict]:
    """Scrape Facebook Marketplace search results for a query."""
    log.info(f"scraping Marketplace listings for query: {query}")
    url = f"https://www.facebook.com/marketplace/search/?query={quote(query)}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url=url, **BASE_CONFIG))
    return parse_marketplace_listing(result)
```



A query-driven URL beats navigating the `/marketplace` homepage. The homepage shows geo-personalized random listings, while a query gives reproducible results you can diff between runs.

python```python
def parse_marketplace_listing(response: ScrapeApiResponse) -> List[Dict]:
    """Extract Marketplace listings from the JSON embedded in the page's script tags."""
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>', response.content, re.DOTALL
    )
    all_listings = []
    for script in scripts:
        try:
            data = json.loads(script)
        except json.JSONDecodeError:
            continue
        all_listings.extend(
            find_objects_by_typename(data, ["MarketplaceProductItem", "GroupCommerceProductItem"])
        )

    parsed_listings = []
    for listing in all_listings:
        geocode = (listing.get("location") or {}).get("reverse_geocode") or {}
        city, state = geocode.get("city", ""), geocode.get("state", "")
        location = f"{city}, {state}" if city and state else (city or state)

        parsed = {
            "id": listing.get("id"),
            "title": listing.get("marketplace_listing_title"),
            "price": (listing.get("formatted_price") or {}).get("text", "N/A"),
            "location": location,
            "is_sold": listing.get("is_sold", False),
            "is_pending": listing.get("is_pending", False),
            "creation_time": listing.get("creation_time"),
        }
        if seller := listing.get("marketplace_listing_seller"):
            parsed["seller"] = {"name": seller.get("name"), "id": seller.get("id")}
        if image := (listing.get("primary_listing_photo") or {}).get("image"):
            parsed["image_url"] = image.get("uri")
        if delivery_types := listing.get("delivery_types"):
            parsed["delivery_types"] = delivery_types
        if category_id := listing.get("marketplace_listing_category_id"):
            parsed["category_id"] = category_id
        parsed_listings.append(parsed)

    log.success(f"parsed {len(parsed_listings)} marketplace listings")
    return parsed_listings
```



The walker matches both `MarketplaceProductItem` and `GroupCommerceProductItem`, Facebook's current and legacy internal types, which share the same listing schema. The example output below shows every key the parser returns; image URLs are trimmed for readability.

json```json
[
  {
    "id": "853859730610824",
    "title": "Free phones",
    "price": "N/A",
    "location": "Antioch, CA",
    "is_sold": false,
    "is_pending": false,
    "creation_time": null,
    "image_url": "https://scontent.fhnl1-1.fna.fbcdn.net/v/t39.84726-6/607472790_...jpg",
    "delivery_types": ["IN_PERSON", "PUBLIC_MEETUP", "DOOR_PICKUP", "DOOR_DROPOFF"],
    "category_id": "1557869527812749"
  },
  {
    "id": "759711906948329",
    "title": "Various electronics",
    "price": "N/A",
    "location": "San Francisco, CA",
    "is_sold": false,
    "is_pending": false,
    "creation_time": null,
    "image_url": "https://scontent.fhnl1-1.fna.fbcdn.net/v/t39.84726-6/570176266_...jpg",
    "delivery_types": ["IN_PERSON", "DOOR_PICKUP"],
    "category_id": "1792291877663080"
  }
]
```



Search results carry `price: "N/A"` for many listings because Facebook populates price on the individual detail page, not in search results. To get pricing per listing, follow `https://www.facebook.com/marketplace/item/{id}/` and run the same parser against that page's script tags.



## How to Scrape Facebook Events

Events is the other fully backed surface. Its public search endpoint is `https://www.facebook.com/events/search?q={query}`, and event listings render without login. The objects carry `__typename: "Event"`.

python```python
async def scrape_facebook_events(event_name: str = "New York, NY") -> List[Dict]:
    """Scrape Facebook Events search results for a query."""
    log.info(f"scraping Events for query: {event_name}")
    url = f"https://www.facebook.com/events/search?q={quote(event_name)}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url=url, **BASE_CONFIG))
    return parse_event(result)


def parse_event(response: ScrapeApiResponse) -> List[Dict]:
    """Extract Event objects from the JSON embedded in the page's script tags."""
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>', response.content, re.DOTALL
    )
    all_events = []
    for script in scripts:
        try:
            data = json.loads(script)
        except json.JSONDecodeError:
            continue
        all_events.extend(find_objects_by_typename(data, ["Event"]))

    parsed_events = []
    for event in all_events:
        place = event.get("event_place") or {}
        location = place.get("contextual_name", "") if place else (
            "Online Event" if event.get("is_online") else ""
        )
        parsed = {
            "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),
            "event_kind": event.get("event_kind"),
            "is_past": event.get("is_past", False),
            "is_happening_now": event.get("is_happening_now", False),
            "is_hosted_by_ticket_master": event.get("is_hosted_by_ticket_master", False),
        }
        if place:
            parsed["location_details"] = {"name": place.get("contextual_name"), "id": place.get("id")}
        if photo := (event.get("cover_photo") or {}).get("photo"):
            parsed["cover_photo"] = {
                "url": (photo.get("eventImage") or {}).get("uri"),
                "accessibility_caption": photo.get("accessibility_caption"),
                "id": photo.get("id"),
            }
        if social_context := event.get("social_context"):
            parsed["social_context"] = social_context.get("text")
        if price_range := (event.get("ticketing_context_row") or {}).get("price_range_text"):
            parsed["price_range"] = price_range
        parsed_events.append(parsed)

    log.success(f"parsed {len(parsed_events)} events")
    return parsed_events
```



The event schema is richer than a date and title. It includes `is_online`, `is_past`, `is_happening_now`, `is_hosted_by_ticket_master`, `social_context` for engagement text, and `price_range` when tickets exist. The example below shows every key the parser returns, with image URLs trimmed.

json```json
[
  {
    "id": "811249488403564",
    "title": "New York, NY - Spinning Babies Workshop w/ Kelly - March 23-24, 2026",
    "date": "Mon, Mar 23 - Mar 24",
    "location": "Mount Sinai Hospital",
    "url": "https://www.facebook.com/events/811249488403564/",
    "start_timestamp": 1774270800,
    "is_online": false,
    "event_kind": "PUBLIC_TYPE",
    "is_past": false,
    "is_happening_now": false,
    "is_hosted_by_ticket_master": false,
    "location_details": {"name": "Mount Sinai Hospital", "id": "158753874185351"},
    "cover_photo": {
      "url": "https://scontent-sjc6-1.xx.fbcdn.net/v/t39.30808-6/588725973_...jpg",
      "accessibility_caption": "May be an image of yoga and text",
      "id": "1260998019404879"
    },
    "social_context": "30 interested · 3 going"
  }
]
```



Use `is_past` to drop finished events when your pipeline only needs upcoming dates. The same walker handles any Facebook surface: swap the typename and the URL, and the rest of the pipeline holds.



## How to Scrape Public Facebook Groups

This section covers groups Facebook labels Public, where post content is visible to logged-out browsers. Private and secret groups, members-only threads, and gated member lists are out of scope. The guide does not cover joining groups or simulating membership.

Public groups expose a name, description, member count, and the visible post list, which reuses the same `Story` typename as Page posts. Group objects themselves carry `__typename: "Group"`.

python```python
async def scrape_facebook_group(slug: str) -> List[Dict]:
    """Scrape a public Facebook Group's metadata objects."""
    # Note: no maintained Groups function ships in the canonical scraper yet.
    # Public group objects carry __typename "Group"; posts reuse the Story types.
    url = f"https://www.facebook.com/groups/{slug}"
    result = await SCRAPFLY.async_scrape(ScrapeConfig(url=url, **BASE_CONFIG))
    scripts = re.findall(
        r'<script type="application/json"[^>]*>(.*?)</script>', result.content, re.DOTALL
    )
    groups = []
    for script in scripts:
        try:
            groups.extend(find_objects_by_typename(json.loads(script), ["Group"]))
        except json.JSONDecodeError:
            continue
    return groups
```



Logged-out public-group scraping works but has a ceiling. Facebook forces a login wall after roughly 1,000 posts from a single logged-out session (kevinzg/facebook-scraper discussion #647).

Rotating residential IPs through Scrapfly's `proxy_pool` keeps each request looking like a fresh logged-out visitor, which is how you scale past that ceiling without logging in.

[How to Scrape Instagram in 2026Tutorial on how to scrape instagram.com user and post data using pure Python. How to scrape instagram without loging in or being blocked.](https://scrapfly.io/blog/posts/how-to-scrape-instagram)

## How to Bypass Facebook's Anti-Bot Protection with Scrapfly

Facebook does not block at one layer. It stacks several defenses on every public page, and each `BASE_CONFIG` line answers one of them.

### Facebook's anti-bot signals

- **Browser fingerprinting**: canvas, WebGL, font lists, timezone, and language checks on first navigation.
- **IP reputation scoring**: datacenter IPs get flagged within minutes; residential addresses receive less scrutiny.
- **Behavior analysis**: mouse, scroll, and timing patterns across requests; community reports cite single-IP blocks near 10 to 20 requests per minute.
- **GraphQL obfuscation**: the internal API uses opaque queries with rotating operation names, which is why reading JSON from script tags is more durable than calling GraphQL directly.
- **Login walls**: a promotional modal on scroll, plus a hard login wall after roughly 1,000 posts from one logged-out session.

### How Scrapfly's stack handles each

- `asp=True` runs Anti-Scraping Protection: TLS and HTTP fingerprint matching, header rotation, and challenge solving.
- `country="US"` geo-targets a US viewer so content matches and US-only surfaces are not redirected.
- `proxy_pool="public_residential_pool"` routes through residential IPs, since datacenter pools get blocked.
- `render_js=True` lets Facebook's client-side hydration run so the embedded JSON populates.
- `js_scenario` waits for the modal Close button, clicks it, and scrolls to trigger lazy-loaded data.



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 190+ 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 JavaScript-heavy 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.
- [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](https://scrapfly.io/integration/make), [n8n](https://scrapfly.io/integration/n8n), [Zapier](https://scrapfly.io/integration/zapier), [LangChain](https://scrapfly.io/integration/langchain), and [LlamaIndex](https://scrapfly.io/integration/llamaindex).



### Web Scraping API

Scrape any website with our powerful API. Anti-bot bypass, JavaScript rendering, and rotating proxies built-in.



[Try Web Scraping API](https://scrapfly.io/docs/scrape-api/getting-started)



## FAQ

Do I need a Facebook login to scrape Marketplace or Events?No. Both surfaces have public search endpoints, `/marketplace/search/` and `/events/search`, that render full data without authentication. The scraper targets those endpoints.







Can I scrape Facebook comments?Comments on public posts are accessible but harder in practice. They load through paginated requests after the initial render and trigger aggressive rate limiting. For app-licensed comment access, use Meta's Graph API or Meta Content Library.







Can I scrape Facebook profiles, posts, or groups that require login?No. This guide covers public-facing data only, what Facebook serves to logged-out browsers at public URLs. For app-licensed access to non-public data, use the [Meta Graph API](https://developers.facebook.com/docs/graph-api/) with app review or Meta Content Library.







Is scraping public Facebook data legal?Under US law, hiQ Labs v. LinkedIn (2022) held that scraping publicly available data does not violate the Computer Fraud and Abuse Act. Terms-of-Service compliance is a separate civil matter platforms enforce through rate limits and IP blocks. Consult a lawyer for cases involving GDPR, CCPA, or storing personal data.







Why extract JSON from script tags instead of using CSS selectors?Facebook obfuscates and rotates class names every few weeks, like `x8gbvx8` and `x78zum5`. The JSON in `<script type="application/json">` carries a stable `__typename` discriminator that does not change, so extracting from it survives layout updates.







How does this compare to kevinzg/facebook-scraper from PyPI?That library was strong in 2020 to 2022 but breaks more often now, and it scrapes mobile HTML that Facebook restructures. It runs logged-out only until a login wall hits around 1,000 posts (discussion #647). The Scrapfly approach reads embedded JSON and rotates residential IPs, so it survives layout changes.







Can I scrape Facebook Reels, Stories, or Ads?This guide does not cover them. Public Reels expose metadata in the same embedded JSON, but they surface through a personalized feed with no public search endpoint, and no maintained function exists yet. For Ads, use the [Meta Ad Library](https://www.facebook.com/ads/library/), which exposes ad metadata publicly.







How fast can I scrape before getting blocked?With Scrapfly's residential pool and ASP, rate-limit pressure is handled at the proxy layer, so you skip manual `time.sleep()` calls. By community accounts, single-IP DIY scraping tends to trigger blocks near 10 to 20 requests per minute.









## Summary

One extraction pattern covers all six public surfaces. Pages, Posts, Profiles, Marketplace, Events, and Groups all render structured data into `<script type="application/json">` tags, and one recursive walker collects the objects you want by `__typename`.

Marketplace and Events ship as tested functions in the canonical scrapfly-scrapers repo linked above. Pages, Posts, and Groups use the same approach today, with maintained versions on the roadmap.

Scrapfly's `js_scenario` closes the promotional modal, `asp=True` handles fingerprint and behavior checks, and the residential `proxy_pool` keeps every request looking like a real logged-out visitor. You skip the browser fleet, the CSS selector repairs, and the in-house proxy 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.

 

   [  Add as a preferred source ](https://google.com/preferences/source?q=scrapfly.io) Table of Contents















 

  Table of Contents- [Key Takeaways](#key-takeaways)
- [Why Scrape Facebook?](#why-scrape-facebook)
- [How to Scrape Facebook: Methods at a Glance](#how-to-scrape-facebook-methods-at-a-glance)
- [Project Setup](#project-setup)
- [The Core Pattern: Extracting JSON from Facebook's Embedded Scripts](#the-core-pattern-extracting-json-from-facebook-s-embedded-scripts)
- [How to Scrape Public Facebook Pages and Profiles](#how-to-scrape-public-facebook-pages-and-profiles)
- [How to Scrape Public Posts from a Facebook Page](#how-to-scrape-public-posts-from-a-facebook-page)
- [How to Scrape Facebook Marketplace](#how-to-scrape-facebook-marketplace)
- [How to Scrape Facebook Events](#how-to-scrape-facebook-events)
- [How to Scrape Public Facebook Groups](#how-to-scrape-public-facebook-groups)
- [How to Bypass Facebook's Anti-Bot Protection with Scrapfly](#how-to-bypass-facebook-s-anti-bot-protection-with-scrapfly)
- [Facebook's anti-bot signals](#facebook-s-anti-bot-signals)
- [How Scrapfly's stack handles each](#how-scrapfly-s-stack-handles-each)
- [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. 

 

 ## Related Articles

 [     

 python data-parsing 

### How to Scrape Government and Public Records Data

Map the public-data sources worth scraping, the friction each portal throws at you, and how to get past it and normalize...

 

 ](https://scrapfly.io/blog/posts/scraping-government-public-records-data) [  

 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) [  

 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) 

  ## 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)