July 23, 2026

Build video analytics into your product

Tharun Budidha
Tharun Budidha
Full stack developer

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)

html
<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:

javascript
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:

javascript
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:

sql
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:

python
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

python
# 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:

text
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

VersionWhat changesFreshness
v1: scheduled pullsThe page above, refreshed hourlyHourly
v2: streaming exportsIngest as views complete; the page updates in close to real timeNear real time
v3: per-account versionSame query, `WHERE account_id = :tenant`; engagement pages for your B2B customersYour call
v4: alerts in-product"Your new lesson is losing viewers at 3:31" as a notification, not a reportOn signal (webhooks)

How different teams use this

TeamWhat they shipTenant key
Course platform (education)Instructor analytics page (this article)instructor ID
B2B SaaS with video in productPer-customer engagement page, QBR exportaccount ID
Creator / subscription platformCreator analytics pagecreator 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.

Share

Stay Ahead of Video
Streaming Trends

Start shipping video today.

Enjoyed reading? You might also like

Build the audience retention graph for your own videos
Video Engineering10 Min Mins Read

Build the audience retention graph for your own videos

YouTube shows creators exactly where viewers drop off. If you run your own video platform, you probably don't have that graph, but the data to build it is already in your player. This tutorial goes end to end: get view data flowing, export it, and build the retention graph yourself in about 60 lines of ClickHouse SQL and Python.

Shashank Ramineni
Shashank Ramineni
VP - Sales & Marketing
Build a creator engagement and churn dashboard for your subscription platform
Video Engineering8 Mins Read

Build a creator engagement and churn dashboard for your subscription platform

If you run a subscription platform for creators, a patron who stops watching is a patron about to cancel. The signal shows up in view data weeks before the failed renewal does: watch time falls, sessions get further apart, and then the card doesn't charge. This article turns that data into a churn dashboard, plus the per-creator view you can hand to each creator on your platform.

Rajakavitha Kodhandapani
Rajakavitha Kodhandapani
Senior Technical Writer
Build a member-retention dashboard for your fitness platform
Video Engineering10 Mins Read

Build a member-retention dashboard for your fitness platform

Members who build a workout habit in their first two weeks stick around; the rest quietly stop opening the app. If you run a streaming-fitness platform, your view data already knows which members are on each path, which instructors keep people coming back, and which classes get abandoned halfway. This article turns that data into a retention dashboard your team can act on every week.

Shashank Ramineni
Shashank Ramineni
VP - Sales & Marketing