Analytics Enhancements — Frontend Integration Guide
Overview
The backend has been updated with 6 new analytics features on the feature/analytics-enhancements branch. This document describes what changed in the API responses and how the frontend should integrate each feature.
Branch: feature/analytics-enhancements
API Endpoints affected:
GET /admin/v1/stats/overview?interval={24h|7d|30d|60d|all}— Overview tabGET /admin/v1/stats/audience?interval={24h|7d|30d|60d|all}— Audience tab
No new endpoints were added. All changes are new fields on existing responses.
Feature 1: Period-over-Period Comparison
Endpoint: GET /admin/v1/stats/overview?interval=7d
Location in response: standard.comparison
Tab: Overview
What it returns
For each key metric, the percentage change compared to the previous equal-length period:
7d→ compares last 7 days vs the 7 days before that30d→ compares last 30 days vs the 30 days before thatall→ returnsnull(no previous period)
{
"standard": {
"comparison": {
"totalPageViewsChange": -23.42,
"totalWatchTimeChange": -13.93,
"uniquePageviewUsersChange": -11.09,
"totalVideoViewsChange": -24.51,
"totalLiveViewsChange": -28.64,
"totalVODViewsChange": -21.39,
"newUsersChange": 5.2,
"returningUsersChange": -8.1
}
}
}
Frontend changes
On the Overview tab, for each analytics card (Page Views, Watch Time, Visitors, Video Views, etc.):
- Show a colored arrow + percentage next to or below the main metric value
- Positive values → green arrow up
▲ +5.2% - Negative values → red arrow down
▼ -23.42% - Zero → grey, no arrow
- When
comparisonisnull(interval = "all"), hide the change indicator
Matching fields:
| Card | Comparison field |
|---|---|
| Page Views | comparison.totalPageViewsChange |
| Watch Time | comparison.totalWatchTimeChange |
| Visitors | comparison.uniquePageviewUsersChange |
| Video Views | comparison.totalVideoViewsChange |
| Live Views | comparison.totalLiveViewsChange |
| VOD Views | comparison.totalVODViewsChange |
| New Users | comparison.newUsersChange |
| Returning Users | comparison.returningUsersChange |
Feature 2: Language Distribution
Endpoint: GET /admin/v1/stats/audience?interval=7d
Location in response: audience.analytics.topLanguages
Tab: Audience
What it returns
Top 10 languages by unique viewers. Language codes are automatically converted to readable names (e.g., pt-BR → Portuguese (Brazil)). Duplicate codes with different casing are merged.
{
"audience": {
"analytics": {
"topLanguages": {
"Portuguese (Brazil)": 7504,
"English (United States)": 1787,
"Portuguese": 394,
"Portuguese (Portugal)": 155,
"English": 68,
"English (United Kingdom)": 61,
"Chinese (Simplified, China)": 9,
"Italian (Italy)": 5,
"English (Canada)": 3
}
}
}
}
Frontend changes
On the Audience tab, add a new "Language Distribution" section (after Search Analytics):
- Display as horizontal bar chart (like Browser Distribution) or a table
- Each row: language name, bar showing relative share, user count
- Top entry gets the widest bar (100%), others proportional
Feature 3: Geographic Trends
Endpoint: GET /admin/v1/stats/overview?interval=7d
Location in response: standard.trafficAnalytics.topCountries[].trendPercentage and standard.trafficAnalytics.topCities[].trendPercentage
Tab: Overview (Geographic Analytics section)
What it returns
Each GeoEntry in the existing Top Countries and Top Cities tables now has a new trendPercentage field showing the % change vs the previous period.
{
"standard": {
"trafficAnalytics": {
"topCountries": [
{
"name": "BR",
"uniqueViewers": 8348,
"watchTimeSeconds": 38570010,
"trendPercentage": -9.18
},
{
"name": "XK",
"uniqueViewers": 64,
"watchTimeSeconds": 42480,
"trendPercentage": 33.33
}
],
"topCities": [
{
"name": "São Paulo",
"uniqueViewers": 1854,
"watchTimeSeconds": 6448960,
"trendPercentage": -10.78
}
]
}
}
}
Frontend changes
In the existing Geographic Analytics tables (Top Countries and Top Cities):
- Add a new "Trend" column on the right side of each table
- Positive → green text with up arrow:
▲ +33.33% - Negative → red text with down arrow:
▼ -9.18% - When
trendPercentageisnull(interval = "all"), hide the column or show—
Feature 4: Ad Conversion Funnel
Endpoint: GET /admin/v1/stats/overview?interval=7d
Location in response: standard.adFunnel
Tab: Overview
What it returns
Aggregated ad metrics with computed conversion rates.
{
"standard": {
"adFunnel": {
"totalImpressions": 70,
"totalViews": 26,
"totalClicks": 3,
"viewThroughRate": 37.14,
"clickThroughRate": 4.29
}
}
}
Frontend changes
On the Overview tab, add an "Ad Conversion Funnel" section (near existing Ad Impressions/Views/Clicks cards):
Three cards in a row:
| Card | Value | Subtitle |
|---|---|---|
| Impressions | adFunnel.totalImpressions |
"Total ad impressions served" |
| Completed Views | adFunnel.totalViews |
Show adFunnel.viewThroughRate% in green below |
| Clicks | adFunnel.totalClicks |
Show adFunnel.clickThroughRate% in orange below |
Note: The existing adImpressions, adViews, adClicks dictionaries (per AdId) are still returned. The adFunnel is the aggregated total across all ads.
Feature 5: Search-to-View Conversion
Endpoint: GET /admin/v1/stats/audience?interval=7d
Location in response: audience.analytics.searchToViewConversions and audience.analytics.searchToViewConversionRate
Tab: Audience
What it returns
How many search result clicks actually led to a user watching a video in the same session.
{
"audience": {
"analytics": {
"totalSearches": 652,
"totalSearchClicks": 78,
"searchToViewConversions": 14,
"searchToViewConversionRate": 17.9
}
}
}
Frontend changes
In the Audience tab Search Analytics section, add two new cards:
| Card | Value | Description |
|---|---|---|
| Led to Video Watch | searchToViewConversions |
"Clicks that led to a video view" |
| Conversion Rate | searchToViewConversionRate% |
"Search click to video view" |
Removed fields
The following fields have been removed from the audience analytics response:
— was redundantsearchClickThroughRate— was not actionableuniqueSearchTerms
Frontend must remove any UI elements that used these two fields.
Feature 6: Viewers Activity (Views Heatmap)
Endpoint: GET /admin/v1/stats/overview?interval=7d
Location in response: standard.viewsHeatmap
Tab: Overview
What it returns
The response format changes based on the interval:
For 24h interval — hourly keys only:
{
"standard": {
"viewsHeatmap": {
"00:00": 33,
"01:00": 35,
"02:00": 32,
"12:00": 43,
"19:00": 3,
"23:00": 2
}
}
}
For 7d, 30d, 60d, all intervals — day-of-week + hour keys:
{
"standard": {
"viewsHeatmap": {
"Monday 00:00": 230,
"Monday 01:00": 207,
"Tuesday 00:00": 39,
"Saturday 19:00": 926,
"Sunday 19:00": 3127,
"Sunday 23:00": 434
}
}
}
Frontend changes
Add a "Viewers Activity" section on the Overview tab:
Detecting the format:
- If keys contain a space (e.g.,
"Monday 00:00"), render the multi-day chart - If keys are just hours (e.g.,
"00:00"), render the single-day chart
For 24h — Single wave chart:
- Smooth area/line chart with hours 00:00-23:00 on x-axis
- Y-axis = unique viewers
- Single colored wave with gradient fill
For 7d+ — Multi-day wave chart (YouTube-style):
- 7 overlaid smooth curves, one per day of week
- Each day has a distinct color
- Day selector buttons (Mon, Tue, ..., Sun, All Days) to toggle/isolate days
- X-axis = hours 00:00-23:00
- Y-axis = unique viewers
- Hover tooltip shows viewer count for the hovered hour
Parsing the keys:
// For 7d+: split "Monday 00:00" into day and hour
const [day, time] = key.split(" ");
const hour = parseInt(time.split(":")[0]);
// For 24h: parse "00:00" directly
const hour = parseInt(key.split(":")[0]);
Grouping data by day (for 7d+):
const days = {};
Object.entries(viewsHeatmap).forEach(([key, value]) => {
const [day, time] = key.split(" ");
if (!days[day]) days[day] = {};
const hour = parseInt(time);
days[day][hour] = value;
});
Summary of API Response Changes
GET /admin/v1/stats/overview — New fields on standard:
| Field | Type | Description |
|---|---|---|
comparison |
OverviewComparisonStats or null |
% change vs previous period |
adFunnel |
AdFunnelStats |
Aggregated ad funnel with rates |
viewsHeatmap |
Dictionary<string, long> |
Hourly for 24h, day+hour for 7d+ |
GET /admin/v1/stats/overview — Changed fields on trafficAnalytics:
| Field | Change |
|---|---|
topCountries[].trendPercentage |
New — double? |
topCities[].trendPercentage |
New — double? |
GET /admin/v1/stats/audience — New fields on analytics:
| Field | Type | Description |
|---|---|---|
topLanguages |
Dictionary<string, long> |
Language → unique viewers |
searchToViewConversions |
long |
Search clicks → video watches |
searchToViewConversionRate |
double |
Conversion % |
GET /admin/v1/stats/audience — Removed fields:
| Field | Reason |
|---|---|
searchClickThroughRate |
Computable from existing fields |
uniqueSearchTerms |
Not actionable |