A self-diagnostic added to the analytics cron earlier today (fallback_reasons + fallback_samples in the response body) surfaced the actual platform-API rejection text for every silent fallback. Turned out every failing platform was a fixable code bug, not an account-level problem:
Facebook — post_impressions / post_impressions_unique / post_engaged_users were retired in Meta's Nov-15-2025 Page Insights deprecation. Switched to the surviving post_video_views + post_clicks set; engagement rate still derives from the separate aggregates query (likes+comments+shares) which keeps working.
Instagram — Meta deprecated per-media impressions in April 2025 in favour of a universal views metric. Swapped the fetcher's metric list to use views and mirrored it into our normalised impressions field so cross-platform aggregations stay consistent.
LinkedIn organisation pages — the old shares[0]={urn} bracket syntax now 403s with "Unpermitted fields present in PARAMETER". Switched to the modern REST.li List(urn:li:share:N) array literal (built by hand because URLSearchParams URL-encodes the parens, which LinkedIn rejects).
X (Twitter) — non_public_metrics requires elevated app permission (Project + Premium/Pro tier). Without it X returns blanket 401 on the whole call — even with a freshly-refreshed token. Dropped the non_public_metrics request. We lose only url_link_clicks; everything else (impressions, likes, replies, retweets, quotes, bookmarks) still flows through.
Token-refresh cron fixed too — two long-standing bugs meant the daily cron was a silent no-op for ~unknown months: (1) it referenced a non-existent account_name column so every query errored and the catch block returned 0 connections; (2) once a token slipped past expiry the query excluded it forever, so users had to reconnect manually. Both fixed; the cron now successfully refreshes YT/X/TikTok tokens that had previously been stuck expired.
Cron also now filters is_active = true when looking up connections, so 23 historical X posts from a long-disconnected account stop hammering the API and get a clean orphan_account_id classification instead.
Result: real platform-API rows jumped from 6/50 to 45/50 in a single cron run. The remaining 5 failures are now all classified-and-correct: 14 X posts from the inactive account (orphan), 7 LinkedIn personal-profile posts (API limitation), 2 TikTok Inbox-mode posts (no queryable id).
Estimator pruned — ~1,000 synthetic rows deleted in a surgical pass: only for posts that have produced at least one real platform_api row. The 1,523 remaining estimator rows are legitimate fallbacks for posts that genuinely can't get real data (LinkedIn personal, TikTok Inbox, deleted FB/IG posts, inactive X account).