Facebook Pages model

Post performance

The Posts list of Page Insights / Meta Business Suite: every post with its lifetime views, viewers, reactions, comments, shares, clicks and engagement rate.

Grain
one row per post
Built from
  1. 3
    Raw tables
  2. 3
    Staging
  3. 1
    Core
  4. 1
    Analytics
Reads from Facebook Pages
pagepostpost_lifetime_insights
Ships as
Weld and dbt SQL, open source on GitHub
core.facebook_pages.post_performance
1-- core.facebook_pages.post_performance
2-- The Posts list of Page Insights / Meta Business Suite: every post with its
3-- lifetime views, viewers, reactions, comments, shares, clicks and engagement
4-- rate.
5--
6-- Grain: one row per post. Metrics are lifetime totals as of `metrics_as_of`,
7-- the last sync; they keep growing while a post is live, so this is a snapshot,
8-- not a time series. Never sum a post's figures across snapshots.
9-- Depends on: staging.facebook_pages.post, .post_lifetime_insights, .page
10--
11-- ENGAGEMENT RATE = (reactions + comments + shares + clicks) / viewers.
12-- Reactions, comments and shares come from post_activity_by_action_type, clicks
13-- from post_clicks. viewers (post_total_media_view_unique) is Meta's replacement
14-- for reach since June 2026; reach_legacy keeps the old reach figure where it
15-- was synced before the cutover. To roll up a set of posts, divide the summed
16-- engagements by the summed viewers; averaging engagement_rate over posts
17-- weights a small post like a large one.
18
19WITH metrics AS (
20    SELECT
21        post_id,
22        MAX(synced_at)                                                                          AS metrics_as_of,
23        MAX(IF(metric = 'post_media_view_all' AND breakdown IS NULL, value, NULL))              AS views,
24        MAX(IF(metric = 'post_media_view_followers' AND breakdown IS NULL, value, NULL))        AS views_from_followers,
25        MAX(IF(metric = 'post_media_view_non_followers' AND breakdown IS NULL, value, NULL))    AS views_from_non_followers,
26        MAX(IF(metric = 'post_media_view_ads' AND breakdown IS NULL, value, NULL))              AS views_from_ads,
27        MAX(IF(metric = 'post_total_media_view_unique' AND breakdown IS NULL, value, NULL))     AS viewers,
28        MAX(IF(metric = 'post_impressions_unique' AND breakdown IS NULL, value, NULL))          AS reach_legacy,
29        SUM(IF(metric = 'post_activity_by_action_type' AND breakdown = 'like', value, 0))       AS reactions,
30        SUM(IF(metric = 'post_activity_by_action_type' AND breakdown = 'comment', value, 0))    AS comments,
31        SUM(IF(metric = 'post_activity_by_action_type' AND breakdown = 'share', value, 0))      AS shares,
32        SUM(IF(metric = 'post_clicks' AND breakdown IS NULL, value, 0))                         AS clicks
33    FROM {{staging.facebook_pages.post_lifetime_insights}}
34    GROUP BY 1
35)
36
37SELECT
38    p.post_id,
39    p.page_id,
40    pg.page_name,

This model and the 4 it depends on. Deploy them together; refs like {{staging.…}} resolve to the matching folders in Weld, and the dbt view uses source() and ref().

Notes

How it counts. And where it stops.

ENGAGEMENT RATE = (reactions + comments + shares + clicks) / viewers. Reactions, comments and shares come from post_activity_by_action_type, clicks from post_clicks. viewers (post_total_media_view_unique) is Meta's replacement for reach since June 2026; reach_legacy keeps the old reach figure where it was synced before the cutover. To roll up a set of posts, divide the summed engagements by the summed viewers; averaging engagement_rate over posts weights a small post like a large one.

Raw tables it reads

  • raw.facebook_pages.page
  • raw.facebook_pages.post
  • raw.facebook_pages.post_lifetime_insights

More Facebook Pages models

Put Facebook Pages in front
of your agents.

Connect Facebook Pages, deploy the models, and ask questions in Claude, Cursor or ChatGPT. Free for 14 days, no credit card.