Back to all posts

How to Scrape Facebook Comments (2026 Guide)

Jonathan Geiger
Facebook APIComment ExtractionFacebook ScrapingBrand MonitoringData Extraction

Need to extract Facebook comments programmatically? Whether you're building brand monitoring tools, customer support triage, social listening platforms, or research datasets, getting reliable Facebook comment data is essential.

In this guide, we'll show you the easiest way to scrape Facebook comments using the Facebook Comments API. No proxies, no Puppeteer, no Page tokens, no app review. Includes cursor-based pagination so you can walk full threads on viral Page videos.

What Data Can You Extract?

The Facebook Comments API provides comprehensive comment information:

  • Comment Content - Full comment text with emoji support
  • Author Information - Display name, profile URL, profile picture
  • Engagement Metrics - Reaction counts and reply counts per comment
  • Page-Author Detection - isAuthor flag identifies page-owner replies
  • Cursor Pagination - Walk thousands of comments on long-running posts
  • Multi-Format URLs - facebook.com/watch?v=, /<page>/videos/<id>/, and /reel/<id>/

The Easy Way: Facebook Comments API

The simplest and most reliable method is using the Facebook Comments API. Here's why:

No Infrastructure Needed - No proxies, browsers, or anti-bot systems
No Facebook Developer Account - No Page tokens, no app review
Always Up-to-Date - API automatically handles Facebook changes
Cursor Pagination - Walk full threads on viral page content reliably
Fast & Reliable - Get comment data with a single REST call
Simple Integration - Just one API call with a Facebook URL
Structured Data - Clean JSON response ready for your application

API Endpoint

https://api.socialkit.dev/facebook/comments

Parameters

ParameterTypeRequiredDescription
urlstringYesFacebook URL (/watch?v=, /<page>/videos/<id>/, or /reel/<id>/)
access_keystringYesYour API access key
limitnumberNoComments per request (max 100, default 10)
cursorstringNoCursor from a previous response to fetch the next page

Example Request

GET https://api.socialkit.dev/facebook/comments?access_key=<your-access-key>&url=https://www.facebook.com/watch?v=876091671461782&limit=20

Example Response

{
  "success": true,
  "data": {
    "url": "https://www.facebook.com/watch?v=876091671461782",
    "postUrl": "https://www.facebook.com/watch?v=876091671461782",
    "commentCount": null,
    "comments": [
      {
        "id": "Y29tbWVudDoxMjY3NzU2MzkxODQwNDc5XzIwNzg1MTcyNDk1NjYxMjU=",
        "name": "Kelly Mcpherson",
        "profileUrl": "https://www.facebook.com/...",
        "profilePicUrl": "https://scontent.fbcdn.net/...",
        "text": "Why does he look like Steve from stranger things? 🤷🏻",
        "likes": 24,
        "replyCount": 2,
        "date": "12-17-2025",
        "timestamp": 1734456789,
        "isAuthor": false
      },
      {
        "id": "Y29tbWVudDoxMjY3NzU2MzkxODQwNDc5Xzg4MzQ5NDI1Nzk3NzgzMA==",
        "name": "Zach Hager",
        "profileUrl": "https://www.facebook.com/...",
        "profilePicUrl": "https://scontent.fbcdn.net/...",
        "text": "Is that the guy from Stranger Things?",
        "likes": 8,
        "replyCount": 0,
        "date": "12-17-2025",
        "timestamp": 1734456823,
        "isAuthor": false
      }
    ],
    "limit": 20,
    "hasMore": true,
    "cursor": "MToxNzc3ODg2MTM0OgF_QDTIzMZ6Yln0..."
  }
}

Note: Facebook does not expose total comment count via this endpoint, so commentCount is null. Use hasMore to know when you've reached the end of the thread.

Code Examples

JavaScript / Node.js

const axios = require('axios');

async function getFacebookComments(postUrl, accessKey, options = {}) {
  const params = {
    access_key: accessKey,
    url: postUrl,
    limit: options.limit || 50,
  };
  if (options.cursor) params.cursor = options.cursor;

  const response = await axios.get(
    'https://api.socialkit.dev/facebook/comments',
    { params }
  );
  return response.data;
}

// Usage
const postUrl = 'https://www.facebook.com/watch?v=876091671461782';
const accessKey = 'your-access-key';

getFacebookComments(postUrl, accessKey, { limit: 50 }).then((data) => {
  console.log(`Returned in this page: ${data.data.comments.length}`);

  data.data.comments.forEach((c) => {
    const tag = c.isAuthor ? ' [author]' : '';
    console.log(`\n${c.name}${tag}: ${c.text}`);
    console.log(`  Reactions: ${c.likes} | Replies: ${c.replyCount}`);
  });

  if (data.data.hasMore) {
    console.log(`\nNext cursor: ${data.data.cursor.slice(0, 60)}...`);
  }
});

Python

import requests

def get_facebook_comments(post_url, access_key, limit=50, cursor=None):
    endpoint = 'https://api.socialkit.dev/facebook/comments'
    params = {'access_key': access_key, 'url': post_url, 'limit': limit}
    if cursor: params['cursor'] = cursor

    response = requests.get(endpoint, params=params)
    response.raise_for_status()
    return response.json()

# Usage
post_url = 'https://www.facebook.com/watch?v=876091671461782'
access_key = 'your-access-key'

data = get_facebook_comments(post_url, access_key, limit=50)
print(f"Returned: {len(data['data']['comments'])}")

for c in data['data']['comments']:
    tag = ' [author]' if c['isAuthor'] else ''
    print(f"\n{c['name']}{tag}: {c['text']}")
    print(f"  Reactions: {c['likes']:,} | Replies: {c['replyCount']}")

PHP

<?php

function getFacebookComments($postUrl, $accessKey, $limit = 50, $cursor = null) {
    $params = [
        'access_key' => $accessKey,
        'url' => $postUrl,
        'limit' => $limit,
    ];
    if ($cursor) $params['cursor'] = $cursor;

    $url = 'https://api.socialkit.dev/facebook/comments?' . http_build_query($params);
    $response = file_get_contents($url);
    return json_decode($response, true);
}

$data = getFacebookComments(
    'https://www.facebook.com/watch?v=876091671461782',
    'your-access-key',
    50
);
foreach ($data['data']['comments'] as $c) {
    $tag = $c['isAuthor'] ? ' [author]' : '';
    echo "{$c['name']}{$tag}: {$c['text']}\n";
    echo "  Reactions: " . number_format($c['likes']) . " | Replies: {$c['replyCount']}\n\n";
}

cURL

curl -X GET "https://api.socialkit.dev/facebook/comments?access_key=your-access-key&url=https://www.facebook.com/watch?v=876091671461782&limit=50"

Walking the Full Thread with Cursor Pagination

Page videos with high engagement can have thousands of comments. The cursor field lets you walk the entire thread reliably:

async function getAllFacebookComments(postUrl, accessKey, maxComments = 500) {
  const all = [];
  let cursor = null;

  while (all.length < maxComments) {
    const remaining = maxComments - all.length;
    const params = {
      access_key: accessKey,
      url: postUrl,
      limit: Math.min(100, remaining),
    };
    if (cursor) params.cursor = cursor;

    const { data } = await axios.get(
      'https://api.socialkit.dev/facebook/comments',
      { params }
    );

    all.push(...data.data.comments);
    if (!data.data.hasMore || !data.data.cursor) break;
    cursor = data.data.cursor;
  }

  return all;
}

// Usage
const comments = await getAllFacebookComments(
  'https://www.facebook.com/watch?v=876091671461782',
  'your-access-key',
  500
);
console.log(`Pulled ${comments.length} comments across all pages`);
def get_all_facebook_comments(post_url, access_key, max_comments=500):
    all_comments = []
    cursor = None

    while len(all_comments) < max_comments:
        remaining = max_comments - len(all_comments)
        data = get_facebook_comments(
            post_url, access_key,
            limit=min(100, remaining),
            cursor=cursor,
        )
        all_comments.extend(data['data']['comments'])
        if not data['data']['hasMore'] or not data['data']['cursor']:
            break
        cursor = data['data']['cursor']

    return all_comments

Use Cases

1. Brand Reputation Pulse

Score sentiment across your last 50 page videos, alert on spikes:

async function brandReputationPulse(pageVideoUrls, accessKey, openaiKey) {
  const allComments = [];
  for (const url of pageVideoUrls) {
    const data = await getFacebookComments(url, accessKey, { limit: 50 });
    allComments.push(...data.data.comments);
  }

  // Send to OpenAI for sentiment scoring
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${openaiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      messages: [{
        role: 'user',
        content: `Score sentiment % across these comments:\n${
          allComments.map((c) => `${c.name}: ${c.text}`).join('\n')
        }`,
      }],
      response_format: { type: 'json_object' },
    }),
  });

  return response.json();
}

2. Customer Support Triage

Classify comments and route into your support queue:

import re

SUPPORT_KEYWORDS = ['help', 'broken', 'issue', 'refund', 'cancel', "doesn't work"]

def triage_page_comments(page_video_urls, access_key):
    tickets = []
    for url in page_video_urls:
        data = get_facebook_comments(url, access_key, limit=50)
        for c in data['data']['comments']:
            text_lower = c['text'].lower()
            for kw in SUPPORT_KEYWORDS:
                if kw in text_lower:
                    tickets.append({
                        'video_url': url,
                        'commenter': c['name'],
                        'profile': c['profileUrl'],
                        'comment': c['text'],
                        'reactions': c['likes'],
                        'replies': c['replyCount'],
                        'flagged_keyword': kw,
                    })
                    break  # one tag per comment
    return tickets

3. Page-Owner Reply Tracker

Surface where the brand has replied vs gone silent:

async function findUnansweredQuestions(postUrl, accessKey) {
  const all = await getAllFacebookComments(postUrl, accessKey, 200);

  // Group by parent (note: replyCount > 0 means it has replies)
  const topLevel = all.filter((c) => !c.isAuthor && c.replyCount === 0);
  const questions = topLevel.filter((c) =>
    c.text.includes('?') && c.likes >= 3
  );

  return questions.map((c) => ({
    commenter: c.name,
    profileUrl: c.profileUrl,
    question: c.text,
    reactions: c.likes,
  }));
}

4. Audience Demographic Mapping

Build a profile of who engages with your Page videos:

def map_audience(page_video_urls, access_key):
    profile_set = set()
    for url in page_video_urls:
        data = get_facebook_comments(url, access_key, limit=100)
        for c in data['data']['comments']:
            if c['profileUrl']:
                profile_set.add(c['profileUrl'])
    
    return {
        'unique_commenters': len(profile_set),
        'profile_urls': sorted(profile_set),
    }

Response Data Explained

Comment Object Fields

  • id - Unique comment identifier (base64-encoded global Facebook ID)
  • name - Commenter's display name
  • profileUrl - URL to the commenter's Facebook profile
  • profilePicUrl - Profile picture URL (signed CDN link)
  • text - Comment text content
  • likes - Total reaction count (reactions, not just thumbs-up)
  • replyCount - Number of threaded replies under this comment
  • date - Comment date in MM-DD-YYYY format
  • timestamp - Unix timestamp (seconds)
  • isAuthor - true if commenter is the post author (page-owner reply)

Top-Level Fields

  • url - The URL you submitted
  • postUrl - Canonical Facebook post URL
  • commentCount - null for Facebook (use hasMore instead)
  • limit - Limit applied to this response
  • hasMore - true if more comments are available
  • cursor - Opaque token to pass back for the next page

Note: Unlike Instagram or YouTube, Facebook's pagination response doesn't include a total comment count. Use hasMore to know when you've walked the full thread.

Supported URL Formats

The API accepts various Facebook URL formats:

  • https://www.facebook.com/watch?v=<video_id> (Watch videos)
  • https://www.facebook.com/<page-name>/videos/<video_id>/ (Page videos)
  • https://www.facebook.com/reel/<video_id>/ (Facebook Reels)
  • https://m.facebook.com/... (mobile URLs - resolved automatically)

Error Handling

Implement retry logic for production reliability:

async function getFacebookCommentsWithRetry(
  postUrl, accessKey, options = {}, maxRetries = 3
) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await getFacebookComments(postUrl, accessKey, options);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));
    }
  }
}

Common error responses:

StatusMeaningWhat to Do
400Missing url parameterCheck the request includes a valid Facebook URL
403Out of creditsTop up at /pricing
404Post not found or comments disabledPost may be private, deleted, or the page restricted comments
408TimeoutRetry with exponential backoff
500Server errorRetry; if persistent, check status

Pricing & Credits

The Facebook Comments API uses 1 credit per 50 comments returned (minimum 1 credit per request).

Monthly plans:

  • Free: 20 credits to start
  • Starter ($19/mo): 4,000 credits, good for ~200,000 comments
  • Standard ($29/mo): 12,000 credits, good for ~600,000 comments (most popular)
  • Ultimate ($95/mo): 50,000 credits, bulk research scale

Pay-as-you-go (one-time, never expires):

  • Starter Pack ($14): 1,000 credits
  • Growth Pack ($49): 20,000 credits
  • Scale Pack ($249): 150,000 credits

See full pricing.

Free Tools

Test the API quality without writing code first:

Conclusion

The Facebook Comments API gives you full access to Page video, watch, post, and reel comments without proxies, Puppeteer, scraping infrastructure, or Facebook's Graph API approval process. Cursor pagination lets you walk the full thread on viral content, page-author flags help you isolate brand replies, and a clean JSON response drops straight into any brand-monitoring or support-triage pipeline.

Start with the free Facebook Comment Viewer to see what the response looks like, then grab 20 free API credits to wire it into your stack.

Related Resources: