Your engagement rate doesn't match mine

Two people pull the same month from the same account and get numbers three times apart. Neither has made a mistake. That argument is what a reporting layer exists to end.

A stack of translucent dark planes at different depths, one dramatically larger and edge-lit
The question
Which content, on which platform, at which time, and what actually changed this week?
Data
2,092 posts across Instagram, LinkedIn, TikTok and X, one year. Synthetic and disclosed.
Method
Twelve KPIs defined once, aggregate rates only, day-parts with a 20-post floor, Mann-Whitney U within platform.
Outcome
A concentration risk the dashboard was calling a win, and a concrete calendar reallocation.

On the data

Seeded synthetic data for a fictional direct-to-consumer beverage brand. No employer or client data appears anywhere in this project. The generator is modelled on how social data actually behaves, with heavy-tailed reach, format and day-part effects, a compounding follower base and paid-boost distortion, so the analysis code meets the same shapes it would meet in production.

Why it was hard

Most social reporting fails in the same four ways, and all four produce a number that looks perfectly reasonable on a slide.

Definition failures

"Your engagement rate doesn't match mine"
Rate on reach and rate on followers are both correct and roughly three times apart. Both are computed, named separately, and defined once in one module.
The headline rate disagrees with itself
Averaging per-post rates lets a 200-reach post count as much as a 200,000-reach one. Aggregate only: total engagement divided by total reach.

Sample-size failures

"Best time to post: Friday 9pm" from five posts
Day and hour cells hold two or three posts each, so one viral post crowns an empty slot. Recommendations run on day-parts with a 20-post floor, and thin cells render blank.
"Carousels beat Reels" on a difference that is noise
No significance test, and formats pooled across platforms. Mann-Whitney U with effect size, computed within each platform.

That last one is the subtle one. "Short video gets 2.5 times the reach share" sounds like a format finding, but short video only exists on TikTok, and TikTok engages at 2.4 times X's rate. A pooled format ranking is mostly measuring the platform. Every format comparison here is within-platform for exactly that reason.

A metric that two people can compute differently is not a metric. It is a recurring meeting.

The averaging trap, measured

The mean of per-post engagement rates on this account is 4.73%. The aggregate rate, total engagement divided by total reach, is 5.32%. Same posts, same month, an 11% gap.

Worth stating plainly: the direction of that gap is not fixed. It is commonly assumed that averaging inflates the headline, because small posts tend to show high percentage rates. Here it deflates, because on this account the high-reach posts also engage well. Which way it breaks depends on how reach and rate correlate in your data, and the only safe move is to stop averaging rates at all rather than to memorise a rule of thumb.

What it found

Half the account's reach depends on one recommendation algorithm, and that platform also engages best, so the dashboard reads it as a win.

Share of total reach Engagement rate TikTok 48.4% 6.1% Instagram 30.9% 5.3% X 11.7% 2.5% LinkedIn 9.0% 4.8% Concentration risk, not a performance win. One algorithm change halves the account. X carries 715 posts, the most of any platform, for 11.7% of reach.
Share of total reach by platform, 2,092 posts over one year. Engagement rate is the aggregate reach-based rate. TikTok's 6.1% against X's 2.5% is the 2.4 times multiple that makes pooled format rankings misleading.

TikTok carries 48.4% of reach and the highest engagement rate. A performance dashboard reports that as the account's strongest channel, which it is. The generated stakeholder narrative flags it as a concentration risk instead, because both facts are true at once. Half the account's reach sits behind a single recommendation algorithm that nobody in the room controls.

The opposite problem sits two rows down. X takes 715 posts, more than any other platform, and returns 11.7% of reach at the lowest engagement rate on the account. That is the clearest effort-against-return finding in the set, and it is invisible on any chart that ranks platforms by a single metric.

On Instagram, Reels earn 1.5 times the reach their calendar space costs. Stories earn a quarter of theirs.

1.0 break-even 00.5 1.01.5 Reel 1.50 Carousel 0.82 Static 0.47 Story 0.27
Multiplier is share of platform reach divided by share of platform calendar, Instagram only, 561 posts. Above 1.0 a format earns more reach than the calendar space it takes. Reels are 42.6% of the Instagram calendar and 63.7% of its reach. Stories are 6.8% of the calendar and 1.8% of reach.

What I would report

One engagement rate on the front page, reach-based and aggregate, with the follower-based figure available and labelled rather than hidden, so the second definition stops arriving as a surprise mid-meeting.

Timing guidance moves to day-parts with a stated sample floor, and cells under the floor render blank rather than ranked. LinkedIn's weekend column stays mostly empty because the brand barely posts there, which is information, not a gap to fill with a guess.

The concrete recommendation on this account is a calendar reallocation, not a vibe. Move Story and Static slots into Reels on Instagram, and audit whether 715 posts a year on X is defensible against 11.7% of reach. Follower charts stay on independent scales per platform, because X's slowing growth is a real story that an Instagram-sized axis would hide entirely.

Limitations, and what I would do differently

Reach is a platform-reported number and platforms define it differently. Comparing reach across four of them is directionally useful and precisely wrong, and no amount of careful arithmetic fixes that. The format multiplier also treats calendar slots as interchangeable units of effort, which they are not. A Reel costs considerably more to produce than a Static, so a 1.5 times reach return is not automatically a 1.5 times return on effort.

The 20-post floor is a judgement I picked for stability, not a derived threshold. It should be set from the variance in each account rather than copied across, and on a low-volume account it would blank almost everything. At that point the honest output is "we cannot answer this yet" rather than a lower floor.

Given more time I would add paid-against-organic splits throughout, since boosted posts distort every reach-based rate on the page, and bring in a saves-and-shares-weighted quality measure. A save is worth considerably more than a like, and a flat engagement count treats them alike.

Artefacts

Source and tests on GitHub, the metric dictionary and QA checklist, and 28 tests passing on a clean checkout.