Facebook Page Competitor Analysis: Benchmark Engagement With Code
Facebook's built-in Insights has one big blind spot: it only shows you your page. You can see your engagement rate is 2% — but is 2% good? If the page you're competing with is pulling 8% on the same kind of content, you're losing and Insights will never tell you. To benchmark, you need the other side of the fence, and the only public, fair way to get it is the data anyone can see on a competitor's page: their posts and the public engagement on each.
This guide builds a competitor benchmarking tool that pulls a page's recent posts, calculates a comparable engagement figure, and breaks down which types of posts (video, photo, link) are working best for them. Code in JavaScript and Python.
What's fair game — and what isn't
Important to get this right. From a competitor's public page you can see public posts and the public engagement counts on them — reactions, comments, shares. You cannot see their reach, impressions, or follower demographics; those are private, owner-only metrics in their Insights. So "engagement rate" here is built on public engagement relative to their follower count (a reasonable, comparable proxy), not on true reach. That's an honest benchmark, and it's the same basis every social analytics tool uses for competitors.
Step 1: Pull a page's recent posts
The endpoint takes a page url (or pageId) and returns recent posts, with a cursor for paging further back.
const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE = "https://api.sociavault.com/v1/scrape/facebook";
async function getPagePosts(pageUrl) {
const res = await fetch(
`${BASE}/profile/posts?url=${encodeURIComponent(pageUrl)}`,
{ headers: { "x-api-key": API_KEY } },
);
const json = await res.json();
if (!json.success) {
console.error("Failed:", json.error);
return [];
}
// Log json.data once to confirm the posts array and field names.
return json.data.posts || json.data.items || [];
}
Aim for the last 20–30 posts so your averages aren't skewed by one viral hit. Page back with the cursor from the response if one batch isn't enough.
Step 2: Aggregate the engagement
Loop the posts, total the public engagement, and bucket by post type. I read each count defensively because field names vary — confirm them against your first real response and tighten up.
function analyze(posts) {
const stats = {
count: posts.length,
reactions: 0,
comments: 0,
shares: 0,
byType: {},
};
for (const p of posts) {
const reactions = p.reaction_count ?? p.reactions ?? 0;
const comments = p.comment_count ?? p.comments ?? 0;
const shares = p.share_count ?? p.shares ?? 0;
const type = p.type || p.media_type || "unknown";
stats.reactions += reactions;
stats.comments += comments;
stats.shares += shares;
const t = (stats.byType[type] ||= { count: 0, engagement: 0 });
t.count += 1;
t.engagement += reactions + comments + shares;
}
return stats;
}
Step 3: Print the scorecard
async function benchmark(pageUrl) {
const posts = await getPagePosts(pageUrl);
if (posts.length === 0) return console.log("No public posts found.");
const s = analyze(posts);
const totalEng = s.reactions + s.comments + s.shares;
const avgPerPost = Math.round(totalEng / s.count);
console.log(`\n📊 Scorecard — ${s.count} posts analyzed`);
console.log(` Avg engagement/post: ${avgPerPost}`);
console.log(
` Reactions ${s.reactions} · Comments ${s.comments} · Shares ${s.shares}`,
);
console.log(`\n🏆 Best-performing post types:`);
Object.entries(s.byType)
.map(([type, t]) => [type, Math.round(t.engagement / t.count), t.count])
.sort((a, b) => b[1] - a[1])
.forEach(([type, avg, n]) =>
console.log(` ${type}: ${avg} avg (${n} posts)`),
);
}
benchmark("https://www.facebook.com/RedBull");
The "best post types" breakdown is the most actionable output: it shows you exactly where Facebook is still handing a competitor organic reach. If their video posts average 5x the engagement of their links, that's a direct hint about where to put your effort.
The same in Python
import os, requests
from collections import defaultdict
API_KEY = os.environ["SOCIAVAULT_API_KEY"]
BASE = "https://api.sociavault.com/v1/scrape/facebook"
def page_posts(url):
r = requests.get(f"{BASE}/profile/posts",
params={"url": url},
headers={"x-api-key": API_KEY}).json()
if not r.get("success"):
return []
d = r["data"]
return d.get("posts") or d.get("items") or []
def benchmark(url):
posts = page_posts(url)
by_type = defaultdict(lambda: {"count": 0, "eng": 0})
total = 0
for p in posts:
eng = (p.get("reaction_count", 0) + p.get("comment_count", 0) + p.get("share_count", 0))
total += eng
t = p.get("type", "unknown")
by_type[t]["count"] += 1
by_type[t]["eng"] += eng
if posts:
print(f"Avg engagement/post: {total // len(posts)} across {len(posts)} posts")
for t, v in sorted(by_type.items(), key=lambda x: -x[1]["eng"]):
print(f" {t}: {v['eng'] // max(v['count'],1)} avg ({v['count']} posts)")
benchmark("https://www.facebook.com/RedBull")
Turning it into ongoing intel
Run the benchmark across your three or four main competitors once a week and log the averages. Over a month you'll see who's gaining traction, who's slipping, and which content formats are winning organic reach right now. Pair the post data with Facebook comment scraping to read why a competitor's top posts resonate, not just that they did. For the broader picture, see Facebook API alternatives.
Frequently Asked Questions
Can you see a competitor's Facebook engagement?
You can see the public engagement on their posts — reactions, comments, and shares, which are visible to anyone viewing the page. You cannot see their private metrics like reach, impressions, or audience demographics; those live in the page owner's Insights. Benchmarking uses the public engagement counts, which is a fair and standard basis for comparison.
How do I calculate a competitor's engagement rate?
Sum the public engagement (reactions + comments + shares) across a sample of their recent posts, then divide by the number of posts for an average per post — or divide by their follower count for a rate. Because you can't see their true reach, follower-based engagement rate is the standard comparable proxy.
Which Facebook post types get the most reach?
It varies by page and audience, which is exactly why you benchmark. Generally, native video and Reels tend to earn more organic distribution than link posts, but the per-type breakdown in the code shows you what's actually working for a specific competitor right now rather than relying on a rule of thumb.
Is scraping Facebook pages legal?
Reading publicly visible page posts and their public engagement counts is standard competitive research. Stay on public pages, respect rate limits, and don't attempt to access private groups, profiles, or owner-only analytics. Use the data for legitimate benchmarking.
How many posts should I analyze?
Aim for the last 20–30 posts. That's a large enough sample to smooth out the occasional viral outlier while still reflecting the page's current strategy. Page back with the cursor if you want a longer history.
Can I track this over time?
Yes — run the benchmark on a schedule (weekly works well) and store each run's averages. Comparing week over week reveals momentum shifts and which formats a competitor is leaning into, turning a one-time snapshot into a trend you can act on.
The bottom line
You can't improve what you can't compare against. Pull your competitors' public posts, benchmark engagement on a fair basis, and let the per-type breakdown show you where Facebook's organic reach is still flowing. Then put your effort where the data points.
Want to benchmark your competitors? Start free with SociaVault with 50 credits and run your first scorecard.
Found this helpful?
Share it with others who might benefit
Ready to Try SociaVault?
Start extracting social media data with our powerful API. No credit card required.