Published on July 15, 2026

Code Snippet

import requests
import time
from collections import Counter

BASE = "https://graph.facebook.com/v20.0"


# ── helpers ──────────────────────────────────────────────────────────────
def g(url: str, token: str, params: dict = None) -> dict:
    """GET a Graph API endpoint, automatically injecting the access token."""
    p = params or {}
    p["access_token"] = token
    resp = requests.get(url, params=p, timeout=30)
    resp.raise_for_status()
    return resp.json()


def get_all(url: str, token: str, params: dict = None, max_pages: int = 50) -> list:
    """
    Follow paging.next until exhausted, returning all items as a flat list.
    Sleeps 0.3 s between pages to stay inside rate limits.
    """
    results = []
    page = 1
    while url and page <= max_pages:
        data = g(url, token, params or {})

        if "error" in data:
            print(f"  API error on page {page}: {data['error'].get('message','')[:80]}")
            break

        results.extend(data.get("data", []))

        # The next cursor is a full URL — clear params so they are not doubled
        url = data.get("paging", {}).get("next")
        params = {}
        page += 1
        time.sleep(0.3)

    return results


# ── example: fetch posts and score engagement ────────────────────────────
def analyse_page(page_id: str, token: str) -> dict:
    posts = get_all(
        f"{BASE}/{page_id}/posts",
        token,
        params={"fields": "id,message,created_time,likes.summary(true),comments.summary(true)"},
    )

    engagement = []
    all_tags: list[str] = []

    for post in posts:
        likes    = post.get("likes",    {}).get("summary", {}).get("total_count", 0)
        comments = post.get("comments", {}).get("summary", {}).get("total_count", 0)
        text     = post.get("message", "") or ""

        tags = [w[1:].lower() for w in text.split() if w.startswith("#")]
        all_tags.extend(tags)

        engagement.append({
            "id":         post["id"],
            "date":       post["created_time"][:10],
            "likes":      likes,
            "comments":   comments,
            "engagement": likes + comments,
            "preview":    text[:80],
        })

    engagement.sort(key=lambda x: x["engagement"], reverse=True)

    return {
        "total_posts":   len(posts),
        "top_posts":     engagement[:5],
        "top_hashtags":  Counter(all_tags).most_common(10),
    }


# ── usage ────────────────────────────────────────────────────────────────
# token   = "YOUR_LONG_LIVED_PAGE_TOKEN"
# page_id = "YOUR_PAGE_ID"
# report  = analyse_page(page_id, token)
# print(f"Total posts: {report['total_posts']}")
# for p in report["top_posts"]:
#     print(f"  {p['date']}  ❤ {p['likes']}  💬 {p['comments']}  {p['preview']}")

The Meta Graph API returns results one page at a time. Every response that has more data includes a paging.next cursor URL. The pattern below wraps that into a single reusable get_all() helper so you never have to think about pagination again.

Key ideas:

  • A thin g() wrapper attaches the access token to every request.
  • get_all() follows paging.next until it disappears or a page limit is hit.
  • A small time.sleep() between pages keeps you well under the API rate limit.
  • Errors inside the JSON body are caught early so a single failed page does not crash the whole run.

Once you have the raw list of posts you can layer on engagement scoring, hashtag frequency counts, or keyword-based sentiment — all shown in the code below.

Love this nugget?

Support our work to get exclusive premium nuggets and early access via a personal activation link.

Support Now & Unlock Exclusive Content