100 free credits — no credit card required.Start building
Logo
Back to blog

How to Scrape Facebook in 2026 (Python, Node, curl)

By Friday··13 min read

Scrape Facebook Pages, posts, and the Ad Library without Graph API App Review: one GET request, cursor pagination, unified schema. Real endpoint, real response, honest on what the official API gates.

How to Scrape Facebook in 2026 (Python, Node, curl)

The fastest way to scrape public Facebook data in 2026 is a single GET request to a Facebook data API. No Meta app, no Graph API App Review, no Business Verification, no page access token that expires. Here is the whole thing:

curl "https://www.socialcrawl.dev/v1/facebook/profile/posts?url=https://www.facebook.com/nasa" \
  -H "x-api-key: $SOCIALCRAWL_KEY"

That returns a page's recent posts in a unified schema: text, media, author, engagement, and timestamp, all normalized. The rest of this guide covers what the response looks like, how to paginate through a Page's timeline, how to reach profiles, posts, comments, and the Ad Library, how to handle errors, and the honest legal and access picture.

Stack: Python 3.10+ · requests · a SocialCrawl API key (free tier: 100 credits) · optionally pandas for CSV


Why doesn't the official Facebook API give you this?

Meta's Graph API is the official interface, and for content you own it is excellent: read your own Page's posts, insights, and comments with a page access token. The problem is other people's Pages.

To read Pages your app does not manage, you need Page Public Content Access (PPCA), a feature you request through Meta App Review. App Review requires a completed app, Business Verification, a documented use case, and a screencast, and PPCA specifically is granted case by case, mostly to established partners. For an individual developer or a small team that just wants public post data from a set of Pages, that is weeks of review with no guaranteed outcome.

Personal profiles are effectively off-limits: the Graph API exposes almost no third-party read access to a person's profile or their posts. And the Ad Library API, while genuinely public, requires identity confirmation and is oriented toward ads about social issues, elections, and politics for full API results, so it does not cover general brand-ad research the way the web Ad Library does.

That leaves two routes for public Facebook data: drive a headless browser against Facebook's aggressively bot-defended front end yourself (login walls, rotating DOM, and checkpoints), or use a Facebook scraper API that maintains that collection layer and sells you a stable contract. This guide takes the second route.

Scraping publicly accessible pages has repeatedly survived legal challenge in US courts (the hiQ v. LinkedIn line of cases), and reading public Facebook Page content sits on that precedent. That is not the whole story: Meta's Terms of Service prohibit automated collection without permission, post text and images carry copyright, and the personal data in posts and comments brings GDPR and CCPA obligations that depend on your jurisdiction and use case. Public Pages (brands, media, public figures) are a very different risk profile from private profiles and closed groups. Read our legal and technical guide to social media scraping before you ship anything commercial, and talk to a lawyer if revenue depends on the answer. This section is a technical overview, not legal advice.

Setup: one key, no Meta app

Install the only required dependency:

pip install requests

Get an API key at socialcrawl.dev, then Dashboard → API Keys. New accounts get 100 free credits, most Facebook endpoints cost 1 credit per page, and credits never expire. Keep the key out of your source:

export SOCIALCRAWL_API_KEY=sc_your_api_key_here

There is no Meta developer app, no App Review, and no access token to refresh. One header does it.

Scraping a Facebook Page's posts

The endpoint is GET /v1/facebook/profile/posts. It takes one required parameter, url (the full Page URL), and paginates with cursor.

import os
import requests

API_KEY = os.environ["SOCIALCRAWL_API_KEY"]
BASE = "https://www.socialcrawl.dev/v1"

resp = requests.get(
    f"{BASE}/facebook/profile/posts",
    params={"url": "https://www.facebook.com/nasa"},
    headers={"x-api-key": API_KEY},
    timeout=30,
)
payload = resp.json()

Here is a real, trimmed response from that exact call (one item shown):

{
  "post": {
    "id": "1578225093672861",
    "url": "https://www.facebook.com/NASA/posts/pfbid02GyMo6ud...",
    "content": {
      "text": "To the skies 🚀\n\nAstronaut Anil Menon launched into space for the first time on July 14, 2026, lifting off from Baikonur Cosmodrome aboard a Soyuz spacecraft.",
      "media_urls": "https://scontent-bos5-1.xx.fbcdn.net/v/t39.30808-6/747790293_...jpg"
    },
    "author": {
      "display_name": "NASA - National Aeronautics and Space Administration"
    },
    "engagement": { "likes": 4316, "comments": 200 },
    "flags": { "deleted": false },
    "published_at": "2026-07-15T20:01:25.000Z",
    "ext": { "author_id": "100044561550831" }
  },
  "computed": { "language": "en", "content_category": "other" }
}

The post shape is canonical: content.text, content.media_urls, author.display_name, engagement.likes, engagement.comments, published_at are the same field names you read on the Instagram, YouTube, and Reddit post endpoints, so a parser you write here transfers unchanged. A few honest caveats about Facebook specifically: engagement.shares and views come back null on the list endpoint because the upstream does not expose those per post, and author.username is null for Pages that use a numeric ID rather than a vanity URL. The numeric Page ID is always in ext.author_id.

Paginating through a Page's timeline

The response includes a pagination object with a next_cursor and a has_more flag. Pass the cursor back as the cursor query parameter, and stop when has_more is false.

def scrape_page_posts(page_url: str, max_pages: int = 20) -> list[dict]:
    """Fetch a Facebook Page's posts, one page per credit."""
    posts, cursor = [], None
    for _ in range(max_pages):
        params = {"url": page_url}
        if cursor:
            params["cursor"] = cursor  # pass next_cursor back verbatim
        resp = requests.get(
            f"{BASE}/facebook/profile/posts",
            params=params,
            headers={"x-api-key": API_KEY},
            timeout=30,
        )
        payload = resp.json()
        if not payload.get("success"):
            raise RuntimeError(payload["error"]["message"])
        posts.extend(payload["data"]["items"])
        page = payload.get("pagination", {})
        cursor = page.get("next_cursor")
        if not page.get("has_more"):
            break
    return posts


print(len(scrape_page_posts("https://www.facebook.com/nasa")), "posts")

The same call in Node and curl

The endpoint, parameters, and envelope are identical across languages. Node:

const res = await fetch(
  "https://www.socialcrawl.dev/v1/facebook/profile/posts?url=" +
    encodeURIComponent("https://www.facebook.com/nasa"),
  { headers: { "x-api-key": process.env.SOCIALCRAWL_API_KEY! } },
);
const { data, pagination } = await res.json();
console.log(data.items.length, "posts, more:", pagination.has_more);

curl, for a quick check:

curl "https://www.socialcrawl.dev/v1/facebook/profile?url=https://www.facebook.com/nasa" \
  -H "x-api-key: $SOCIALCRAWL_KEY"

That last one hits GET /v1/facebook/profile, the Page profile endpoint, returning display name, avatar, bio, follower count, and page likes. In a live call it returned NASA's 28,000,000 followers and a joined_at of "March 4, 2009".

Beyond posts: profiles, comments, and the Ad Library

Facebook is more than a timeline. The same key and envelope reach:

  • Page analytics in one call: GET /v1/facebook/profile/full returns the profile, recent posts, and computed analytics (engagement rate, posting cadence, top post) for 5 credits.
  • Comments: GET /v1/facebook/post/comments takes a post URL and returns the comment list, paginating the same way.
  • Ad Library: the adlibrary endpoints search advertisers and pull a company's active ads without the identity-confirmation gate of Meta's own Ad Library API, useful for competitive ad research.
  • Marketplace and Events: marketplace/search and events/search cover the commerce and events surfaces.

The full roster is on the Facebook platform page.

Handling errors and rate limits

Failures use the same envelope with success: false and a typed error object. The types you will actually meet scraping Facebook:

Error typeStatusWhat it meansYour move
RESOURCE_NOT_FOUND404Page removed, private, or URL typo'dSkip it. Empty upstream refunds the credit.
UPSTREAM_ERROR502Facebook hiccupedRetry with backoff; credit refunded.
SERVICE_UNAVAILABLE503Circuit breaker openHonor the Retry-After header.
CONCURRENCY_LIMIT429More than 50 concurrent requests / keyCap your worker pool below 50.
INSUFFICIENT_CREDITS402Balance below the call costTop up.

Note what is not there: a queries-per-minute or daily quota. The only throughput constraint is 50 concurrent requests per key. And empty-upstream 404s, 502s, and 503s automatically refund the credit, so a retry wrapper does not double-spend. The full matrix is in the error handling docs.

Scraping Facebook at scale: the credits math

The math is flat. The profile and posts endpoints are 1 credit per page; the all-in analytics endpoint is 5.

WorkloadRequestsCredits
One Page, first page of posts11
One Page, 20 pages of posts2020
100 Pages × profile + 5 pages of posts600600
100 Pages, all-in analytics (5 cr each)100500

The free tier's 100 credits cover the first rows. Past that, pricing is pay-as-you-go and credits never expire: 2,500 credits is £15, and the Growth plan's 20,000 (£49) funds the 100-Page sweep many times over. Repeated requests for the same Page inside the cache window cost 0 credits, so iterative development is free. If you want brand mentions across Facebook and other platforms without writing code, the free brand mention checker runs the same collection layer in your browser.

Frequently asked questions

How do I scrape Facebook without the Graph API?

Use a third-party Facebook API that runs the collection layer for you. GET /v1/facebook/profile/posts takes a Page URL and an x-api-key header and returns posts in a unified schema, with no Meta app, no App Review, and no Page Public Content Access approval. You paginate with the next_cursor and reach profiles, comments, and the Ad Library the same way.

Reading public Facebook Page content has meaningful precedent in its favor, but it depends on jurisdiction, data type, and access method, and Meta's Terms prohibit automated collection without permission. Post text and images carry copyright, and posts and comments contain personal data under GDPR and CCPA. Public Pages are a different risk profile from private profiles and groups. Check Meta's current terms and consult qualified counsel for commercial use. This is a technical overview, not legal advice.

Can I scrape a personal Facebook profile?

The official Graph API exposes almost no third-party read access to a person's profile, and private profiles are not public data. This guide is about public Pages (brands, media, public figures) and public content like the Ad Library. Do not scrape private profiles; it is both a Terms violation and a legal risk.

What is Page Public Content Access?

Page Public Content Access (PPCA) is the Graph API feature that lets an app read Pages it does not manage. It is gated behind Meta App Review plus Business Verification and granted case by case, mostly to established partners. A third-party Facebook API removes that gate for public Page data because it operates its own collection, not your Meta app.

How much does it cost to scrape Facebook?

The profile and posts endpoints cost 1 credit per page, and the all-in analytics endpoint costs 5. New accounts get 100 credits that never expire, then pricing is pay-as-you-go from £15 for 2,500 credits. The official Graph API is free but gated behind App Review for other people's Pages, so third-party pricing is the practical number to budget against.

Can I search the Facebook Ad Library by API?

Yes. The adlibrary endpoints search advertisers and return a company's active ads through the same x-api-key, without the identity-confirmation step and political-ad focus of Meta's own Ad Library API. It is aimed at competitive ad and creative research on public advertising.

Does the same code scrape other platforms?

Almost. Facebook posts come back in the same canonical shape as Instagram, YouTube, TikTok, and Reddit posts (content.text, author, engagement.likes, published_at), so your parsing and export code transfers. The differences are the input parameter and the pagination token name per platform, both listed in the pagination docs.


Where to go from here

You now have a Facebook scraper that costs 1 credit per page and survives Facebook's front-end changes because maintaining that compatibility is the API's job, not yours. Next steps: browse the other endpoints on the Facebook platform page, read the Facebook data API guide for the deeper reference, or compare providers in best Facebook data APIs (2026). Start with the free tier: 100 credits, no card is about a hundred pages of posts, more than enough to find out whether the data answers your question.

Topics
#facebook-scraper#scrape-facebook#facebook-scraper-api#how-to-scrape-facebook#facebook-page-scraper#facebook-posts-scraper#facebook-data-api#facebook-ad-library-api

Related posts

🤖 AI agent or LLM? Read this page as markdown