Your instructors ask how their lessons are doing. The platforms they compare you to answer with an analytics page per creator. This article builds that page into your product, from data you already collect.
The same architecture serves any tenant: instructors on a course platform, customers of a B2B product, creators on a subscription platform. One aggregation, one endpoint, one component.
Here's the page you'll build, generated from the sample dataset below: one instructor's lessons, with views, watch time, completion, a retention curve per lesson, and the most replayed moment.
How the data flows
Views flow into your own database as they complete; your app serves each tenant their slice. The dashboard below is the last step, but every step is yours to own.
fastpix-video-data-sample-export.csv: https://drive.google.com/file/d/1iMRkZEryzGc0_7GdAc_eTzd4Xluz-ZCu/view?usp=sharing
80 real (anonymized) views from an online course catalog, exported from the FastPix Video Data API. 136 columns per view. Every query below runs against it. No FastPix account yet? The sample works without one - or start free with $25 credit (no credit card) and build against your own views.
Part 1: Get view data flowing from your player
Playback happens in your player. FastPix Video Data captures every interaction from inside it: it dedupes the beacons, stitches events into sessions, adds geo and device data, and computes quality scores, across millions of concurrent views when you need it. For a product feature, that processing is the part you don't have to build.
Already collecting views? Skip to Part 2. New here: about ten minutes.
Step 1: Add the player (or add monitoring to the one you have)
<script src="https://unpkg.com/@fastpix/[email protected]/dist/player.js"></script>
<fastpix-player playback-id="YOUR_PLAYBACK_ID" stream-type="on-demand"></fastpix-player>Own player? The monitoring SDK captures the same data from hls.js, video.js, dash.js, and native mobile players:
import fastpixMetrix from "@fastpix/data-core";
fastpixMetrix.tracker(videoElement, {
hlsjs: hls,
Hls: Hls, // both are required
data: { workspace_id: "YOUR_WORKSPACE_KEY" },
});Step 2: Custom dimensions are your tenant keys
For a product feature, this step is the whole game. Whatever ID you pass here is what you'll filter by when serving each tenant their page:
fastpixMetrix.tracker(videoElement, {
hlsjs: hls,
Hls: Hls,
data: {
workspace_id: "YOUR_WORKSPACE_KEY",
custom_2: `${user.id}::${user.email}`, // who's watching
custom_5: lesson.slug, // which lesson
custom_7: instructor.id, // whose content <- the tenant key
custom_9: course.title, // which course
},
});If a lesson shows zero in the dashboard later, the usual cause is the same one as always: metadata set after the tracker initialized. Pass it in the tracker() call.
Step 3: Choose how views reach your database
Three ways, freshest first. The streaming exports API delivers each view as it completes, so a lesson's numbers update in close to real time. Webhooks notify your backend when views land, if you'd rather pull on signal. And scheduled pulls from the export API are fine when hourly is fresh enough for the page you're building. Start with scheduled pulls; upgrade the transport later without touching anything downstream.
Part 2: The three pieces your product needs
Store, serve, render. Each piece is small.
Step 4: Store views in ClickHouse
One table, a subset of the 136 fields plus the events stream:
CREATE TABLE views (
view_id String,
video_title String,
duration_ms Int64,
played_ms Int64,
viewer_id String,
instructor_id String, -- from custom_7
lesson_slug String, -- from custom_5
view_start DateTime64(3),
events String
) ENGINE = MergeTree ORDER BY (instructor_id, view_start);Your ingest job (streaming consumer or scheduled pull) inserts rows as they arrive. Ordering by instructor_id makes every per-tenant query fast.
Step 5: One query per dashboard load
This is the exact query behind the page at the top, run here on the sample CSV with chdb - the same ClickHouse SQL your endpoint will run against the table above:
lessons = chdb.query("""
SELECT video_title,
count() AS views,
uniqExact(final_viewer_id) AS viewers,
sum(CAST(view_total_content_playback_time AS Int64)) AS played_ms,
avg(least(CAST(view_total_content_playback_time AS Int64)
/ CAST(video_duration AS Int64), 1.0)) AS completion
FROM file('fastpix-video-data-sample-export.csv', CSVWithNames)
WHERE custom_7 = 'Dr. Priya Raman' AND video_duration > 0
GROUP BY video_title
ORDER BY views DESC
""", "DataFrame")The retention curve and most replayed moment per lesson reuse the interval and jump logic from the retention graph and most replayed moments articles; the full script that rendered the demo page is a download on this article.
Start using FastPix for free - $25 credit, no credit card required. Ship the first version of this page against your own views this week. Create your workspace →
Step 6: Serve it, with your auth deciding the tenant
# FastAPI - the tenant comes from the session, never from the client
@app.get("/api/my/lesson-analytics")
def lesson_analytics(user=Depends(current_user)):
if not user.is_instructor:
raise HTTPException(403)
return query_lessons(instructor_id=user.instructor_id)The one rule that matters: the instructor ID comes from your session, never from a query parameter. The tenant key in the data (Step 2) plus the tenant check in the endpoint is the whole isolation model.
Step 7: Render it in your product
The demo page draws each retention curve as an SVG polyline from 20 numbers - no chart library needed for v1:
function RetentionCurve({ curve, width = 280, height = 64 }) {
const pts = curve
.map((v, i) => `${(i * width) / (curve.length - 1)},${height - v * (height - 6) - 3}`)
.join(" ");
return (
<svg width={width} height={height}>
<polyline points={pts} fill="none" stroke="#6D22CD" strokeWidth="2" />
</svg>
);
}Tiles, lesson cards, and the most-replayed line are ordinary components fed by the Step 6 endpoint. The rendered result is the page at the top of this article.
Part 3: What the sample page shows
The instructor can now see what your content team saw. Dr. Priya Raman's two lessons: 44 views, but 5% and 1% completion, with the orientation's most replayed moment at 22:00 (7 rewatches). She doesn't need a meeting with your analytics team to act on that; the page tells her to shorten the lectures and clip the 22:00 section.
That's the product argument. Every question an instructor emails you is a question the page can answer. Analytics stops being an internal chore and becomes a reason instructors prefer your platform.
Where this goes next
| Version | What changes | Freshness |
|---|---|---|
| v1: scheduled pulls | The page above, refreshed hourly | Hourly |
| v2: streaming exports | Ingest as views complete; the page updates in close to real time | Near real time |
| v3: per-account version | Same query, `WHERE account_id = :tenant`; engagement pages for your B2B customers | Your call |
| v4: alerts in-product | "Your new lesson is losing viewers at 3:31" as a notification, not a report | On signal (webhooks) |
How different teams use this
| Team | What they ship | Tenant key |
|---|---|---|
| Course platform (education) | Instructor analytics page (this article) | instructor ID |
| B2B SaaS with video in product | Per-customer engagement page, QBR export | account ID |
| Creator / subscription platform | Creator analytics page | creator ID |
Running this on your own data
Point Step 5 at your export (or the table from Step 4) and change the tenant value. The two things to decide before shipping: which custom dimension is your tenant key (set it everywhere, from day one), and how fresh the page needs to be, which picks your transport from Step 3.
Frequently Asked Questions (FAQs)
How do I add analytics to my video platform?
Store view-level analytics in your own database, aggregate the data for each tenant, and expose the results through authenticated API endpoints. The workflow described in this article includes the storage schema, aggregation query, API endpoint, and frontend component needed to build the dashboard.
How do I show creators or instructors their own analytics?
Associate every video view with a creator or instructor ID using a custom dimension, then filter analytics by that identifier in your backend. The authenticated user session determines which data is returned, ensuring the client cannot request another creator's analytics.
Do I need to build a data pipeline for video analytics?
Not from scratch. FastPix Video Data captures playback events, creates viewing sessions, and generates analytics automatically. Your implementation only needs to ingest the exported data into your own database. Data can be delivered through scheduled exports, webhooks, or streaming exports.
How fresh is the data?
It depends on the delivery method you choose. Scheduled exports can provide updates as frequently as every hour, while the Streaming Exports API delivers completed viewing sessions continuously, enabling dashboards that refresh close to real time.
Can each customer see only their own data?
Yes. The tenant or customer ID is stored as a custom dimension in the analytics data, and your backend filters results using the authenticated session rather than client-supplied values. This approach prevents users from accessing another customer's analytics.
What does it cost to start?
Getting started is free. You receive a $25 credit with no credit card required. The Video Data free tier includes 100,000 plays per month, and the FastPix Player is free for FastPix customers.





