Back Office · office.temerarii.xyz
SOCIAL-METRICS-SETUP.md

← all docs

Social Metrics — Direct-to-Channel (the free developer way)

The reference report's Social section (FB visits, IG profile visits, + engagement) needs on-platform

numbers GA4 can't see (impressions, reach, follows, likes). Two ways to get them; we choose the

developer way: go direct to each platform's own API, which is free for reading your OWN

account's analytics on every platform except X/Twitter (the one paid exception — handled below).

We skip Blotato-as-analytics (it's a publisher, not an analytics source) — BUT Blotato stays useful

for one thing: it returns the platform post ID when it publishes, which is how we attribute an

on-platform metric back to a calendar cell (see "Attribution" at the bottom).

Universal free fallback (already built): every platform's LINK CLICKS + downstream sessions +
conversions come free via the UTM→GA4 path (scripts/utm_links.py + metrics/ga4). So the
Reports tab shows per-channel traffic + revenue with ZERO social APIs. The native APIs below add
the on-platform layer (impressions/reach/follows/engagement) that GA4 cannot see. Do them in
difficulty order; ship value at every tier.

TIER 1 — do now, free, low friction

1. YouTube — reuse our existing Google OAuth (EASIEST)

We already hold a Google OAuth token (secrets/gws/google_token.json) for GA4/GSC. YouTube rides the

SAME token — just add two scopes and re-run the consent.

  • API: YouTube Analytics API (youtubeAnalytics.reports.query) + Data API v3 (channel/video lists).
  • Auth: add to scripts/_google_oauth.py SCOPES:

https://www.googleapis.com/auth/yt-analytics.readonly and https://www.googleapis.com/auth/youtube.readonly,

then python scripts/_google_oauth.py and re-approve as temerariimedia. No new app, no key.

  • Metrics (free): views · estimatedMinutesWatched · averageViewDuration · subscribersGained ·

likes · comments · shares · impressions + impressionClickThroughRate (Analytics API). Per-video

via dimensions=video.

  • Enable: YouTube Analytics API + YouTube Data API v3 in the Google Cloud project (temerarii-media).
  • Connector: engine/integrations/metrics/social_youtube.pygoogleapiclient.build('youtubeAnalytics','v2').

2. Bluesky — AT Protocol, free, no app review (EASY)

  • API: AT Protocol XRPC. No developer app, no approval.
  • Auth: Bluesky → Settings → App Passwords → create one. .env: BLUESKY_HANDLE,

BLUESKY_APP_PASSWORD. POST com.atproto.server.createSessionaccessJwt.

  • Metrics (free): per-post likeCount · repostCount · replyCount · quoteCount via

app.bsky.feed.getAuthorFeed?actor=<handle>. Follower count via app.bsky.actor.getProfile.

No impressions/reach (Bluesky doesn't expose them) → that cell renders "n/a (platform)".

  • Connector: social_bluesky.py (stdlib urllib; createSession → getAuthorFeed → sum counts/day).

TIER 2 — free, one app each, medium friction

3. Meta — Facebook Page + Instagram Business + Threads (ONE Meta app covers all three)

This is where the reference's "Facebook visits / Instagram profile visits" come from. Free API; the

friction is the App Review + business verification.

  • Prereqs: a Meta Developer app (developers.facebook.com) · a Facebook Page · an **Instagram

Business/Creator account linked to that Page · a long-lived Page Access Token**.

  • Permissions (App Review for production): pages_read_engagement, read_insights,

instagram_basic, instagram_manage_insights; for Threads add threads_basic, threads_manage_insights.

  • Metrics (free):
  • Facebook Page: GET /{page-id}/insights?metric=page_impressions,page_views_total,page_fans,page_post_engagements.
  • Instagram: GET /{ig-user-id}/insights?metric=reach,impressions,profile_views,follower_count +

per-media GET /{media-id}/insights?metric=impressions,reach,saved,likes,comments.

  • Threads: GET /{threads-user-id}/threads_insights?metric=views,likes,replies,reposts,quotes.
  • Auth flow: OAuth → exchange short token → 60-day long-lived token → store META_PAGE_TOKEN,

META_PAGE_ID, META_IG_USER_ID, META_THREADS_USER_ID in .env. Refresh long-lived token before expiry.

  • Connectors: social_meta.py (FB+IG), social_threads.py (shares the token).

4. Pinterest — API v5, free, business account + app

  • Prereqs: Pinterest business account + a Pinterest Developer app (Trial access is immediate;

Standard access after a short review).

  • Auth: OAuth authorization-code; scopes pins:read, boards:read, user_accounts:read. Store

PINTEREST_ACCESS_TOKEN.

  • Metrics (free): GET /v5/user_account/analytics?metric_types=IMPRESSION,SAVE,PIN_CLICK,OUTBOUND_CLICK

(account-level) + per-pin GET /v5/pins/{pin_id}/analytics.

  • Connector: social_pinterest.py.

TIER 3 — free but approval-gated (slow; provision in parallel)

5. LinkedIn — Community Management API (restrictive approval)

  • Prereqs: LinkedIn Developer app · admin of an Organization (Company Page) · request the

Community Management API product (approval can be slow/selective).

  • Auth: OAuth 3-legged; scopes r_organization_social, r_organization_admin. Store LINKEDIN_ACCESS_TOKEN,

LINKEDIN_ORG_URN (urn:li:organization:<id>).

  • Metrics (free): organizationalEntityShareStatistics (impressions, clicks, likes, comments,

shares, engagement rate) + networkSizes (followers) + organizationPageStatistics (page views).

  • Connector: social_linkedin.py.

6. TikTok — Business API (app approval + business account)

  • Prereqs: TikTok Developer app · TikTok Business account · request Content/Analytics scopes (approval).
  • Auth: OAuth; scopes user.info.stats, video.list, analytics scopes. Store TIKTOK_ACCESS_TOKEN.
  • Metrics (free): profile (follower_count, profile_views, likes) + per-video (views, likes, comments,

shares, reach) via the Business API. (The basic Display API is lighter and gives less.)

  • Connector: social_tiktok.py.

TIER 4 — NOT free (the one exception): X / Twitter

  • Reality: X API v2 Free tier is essentially write-only (~1.5k posts/mo) with **no useful

analytics reads. Basic = $100/mo**, Pro = $5,000/mo. Impressions (non_public_metrics) need the

owner context AND a paid tier.

  • Recommendation: do NOT pay for the X API. Use the free UTM→GA4 path for X (link clicks +

sessions + conversions are already captured by utm_source=x), and pull on-platform numbers (likes,

RTs, impressions) by manual monthly export from X Analytics into the snapshot if you want them.

If X performance ever justifies $100/mo, social_x.py (v2 tweet.fields=public_metrics) is a small add.


Attribution — tying on-platform metrics back to a campaign/cell

GA4 sees UTM (so link clicks/sessions/revenue join automatically). But the native social APIs return

metrics per account/post, and platforms do NOT echo our UTM. To attribute an on-platform metric to

a calendar cell:

  1. At publish (Blotato or direct), capture the returned platform post ID and store the map

platform_post_id → {channel, campaign, cell} (a small content/_generated/post-ids.json).

  1. The social connectors pull per-post metrics, look up the post ID in that map, and write snapshot

rows keyed date · channel · campaign · cell · metric. Account-level metrics (followers, profile

views) store with campaign=null (they're not per-campaign).

This is the ONE reason Blotato stays in the loop — for the post-ID mapping, not for analytics.


Connector contract (every social_<platform>.py)

Mirror metrics/resend.py + metrics/twilio.py:

  • verify() → reports auth state + exactly what .env keys are missing + free/paid/approval status.
  • pull(start, end) → returns 0 rows until credentialed; then writes metrics-snapshot.json rows

(date·channel·campaign·cell·metric·value) via engine.integrations.metrics.upsert.

  • Stdlib-only (urllib), graceful-empty, never leaks a token. The Office Reports tab bakes from the snapshot.

Recommended sequence (ship value at each step)

  1. Now (free, no provisioning): utm_links.py → GA4 already gives per-channel traffic+revenue for

ALL 9 platforms. This is live the moment publishing starts.

  1. Tier 1: YouTube (reuse Google OAuth) + Bluesky (app password) — a day's work, no approvals.
  2. Tier 2: Meta (FB+IG+Threads, one app) + Pinterest — provision the apps, then connectors.
  3. Tier 3: LinkedIn + TikTok — start the approval requests early; wire connectors when granted.
  4. Tier 4: X — stay on the UTM→GA4 path; pay only if it earns it.

What the operator must provide (the checklist)

  • YouTube: approve the two added scopes (re-run _google_oauth.py) + enable the 2 APIs in GCP. (free)
  • Bluesky: create an App Password → BLUESKY_HANDLE, BLUESKY_APP_PASSWORD. (free)
  • Meta: create the app, link FB Page + IG Business + Threads, complete App Review → META_* tokens.
  • Pinterest: business account + app → PINTEREST_ACCESS_TOKEN.
  • LinkedIn: app + Community Management API approval → LINKEDIN_*.
  • TikTok: business account + app approval → TIKTOK_ACCESS_TOKEN.
  • X: nothing — we use UTM→GA4 (or $100/mo if you decide it's worth it).