{"id":39757,"date":"2026-01-27T07:00:00","date_gmt":"2026-01-27T15:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=39757"},"modified":"2026-01-27T15:19:34","modified_gmt":"2026-01-27T23:19:34","slug":"start-growing-video-analytics","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics","title":{"rendered":"Stop Guessing, Start Growing With Cloudinary Video Analytics"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Growing a following without understanding viewer behavior is expensive. You publish more and promote, but results stay flat.<\/p>\n<p>Views lie. They tell you someone clicked, not why they stayed or why they left. Most creators never see the moment interest drops. Was it the intro? The pacing? Maybe the hook? Without that data, every video is a gamble.<\/p>\n<p>Video analytics removes the guesswork by showing exactly how real viewers interact with your content. In this project, we built a lightweight video analytics dashboard using <strong>Next.js<\/strong>, <strong>vanilla JavaScript<\/strong>, and <strong>Cloudinary Video Analytics<\/strong>.<\/p>\n<h2>What We Build<\/h2>\n<p>We\u2019ll build a small, focused system that shows where viewers drop off:<\/p>\n<ul>\n<li>\n<strong>A simple video player.<\/strong> This uses Cloudinary to deliver the video and capture engagement events.<\/li>\n<li>\n<strong>A tracking layer.<\/strong> A lightweight JavaScript library (<code>cloudinary-video-analytics<\/code>) listens to play, pause, and watch time events and sends them to Cloudinary Video Analytics.<\/li>\n<li>\n<strong>A creator dashboard.<\/strong> This pulls raw analytics data programmatically from Cloudinary\u2019s API and turns it into readable insights.<\/li>\n<\/ul>\n<h3>What You Can See<\/h3>\n<p>By utilizing the Cloudinary Video Analytics API and JS library, you can identify plays, watch time\/rate, unique viewers, and where they drop off in the video.<\/p>\n<p>Every chart answers a practical question a creator asks before publishing the next video. The system runs on <strong>Next.js<\/strong> with a clean UI built using <strong>shadcn\/ui.<\/strong> Analytics requests stay server-side using <strong>basic authentication<\/strong> to keep your API key and secret safe.<\/p>\n<p>These insights change how you plan content, turning random growth into intentional growth.<\/p>\n<ul>\n<li>\n<strong>Live demo:<\/strong> <a href=\"https:\/\/video-insights-demo.vercel.app\/\">https:\/\/video-insights-demo.vercel.app\/<\/a>\n<\/li>\n<li>\n<strong>Source code:<\/strong> <a href=\"https:\/\/github.com\/musebe\/video-insights-demo\">https:\/\/github.com\/musebe\/video-insights-demo<\/a>\n<\/li>\n<\/ul>\n<h2>Project Setup<\/h2>\n<p>The project uses <strong>Next.js with the App Router<\/strong>, providing server routes, client components, and fast iteration capabilities.<\/p>\n<h3>Initial Configuration<\/h3>\n<p>The initial setup is simple and focused:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npx<\/span> <span class=\"hljs-selector-tag\">create-next-app<\/span><span class=\"hljs-keyword\">@latest<\/span> video-insights-demo\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We\u2019ll choose <strong>TypeScript<\/strong>, the <strong>App Router<\/strong>, and <strong>Tailwind CSS<\/strong> to keep the foundation lightweight and strictly typed.<\/p>\n<h3>UI Foundation With shadcn\/ui<\/h3>\n<p>Next, we\u2019ll add <strong>shadcn\/ui<\/strong> for the interface:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npx<\/span> <span class=\"hljs-selector-tag\">shadcn<\/span><span class=\"hljs-keyword\">@latest<\/span> init\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This provides clean, accessible UI components without locking us into a heavy design system. Cards, buttons, badges, and layout pieces are added only when needed.<\/p>\n<h3>Intentional Folder Structure<\/h3>\n<p>The folder structure remains organized and purposeful:<\/p>\n<ul>\n<li>\n<strong><code>app\/<\/code><\/strong> for routes and pages.<\/li>\n<li>\n<code>app\/api\/<\/code>** for server-only analytics endpoints to keep credentials secure.<\/li>\n<li>\n<strong><code>components\/<\/code><\/strong> for reusable UI and video tracking logic.<\/li>\n<li>\n<strong><code>lib\/<\/code><\/strong> for Cloudinary helpers and authentication utilities.<\/li>\n<\/ul>\n<p>Environment variables handle all secrets, ensuring nothing sensitive touches the client-side code.<\/p>\n<p>At this point, we have a fast, modern Next.js app; a clean UI foundation, and a secure architecture to safely interact with Cloudinary\u2019s APIs.<\/p>\n<h2>Tracking Video Engagement<\/h2>\n<p>This is where the guessing stops. Cloudinary Video Analytics works with more than just the default Cloudinary player; it can track engagement on <strong>any HTML5 video element<\/strong>. This flexibility is crucial because it allows you to maintain full control over your UI while leveraging powerful backend analytics.<\/p>\n<p>We\u2019ll use the <strong>Cloudinary Video Analytics JavaScript library<\/strong> with manual tracking. Manual tracking is chosen for its precision, as it avoids \u201chidden magic\u201d and is ideal for dynamic elements or custom domains where you need to explicitly provide a cloud name and public ID.<\/p>\n<h3>The Core Flow<\/h3>\n<p>The implementation follows a simple, three-step process:<\/p>\n<ol>\n<li>The video loads from Cloudinary using a standard <code>&lt;video&gt;<\/code> tag.<\/li>\n<li>We\u2019ll connect the analytics tracker to the element on the client side after the video mounts using <code>connectCloudinaryAnalytics(videoElement)<\/code>.<\/li>\n<li>We\u2019ll start a tracking session by calling <code>startManualTracking<\/code> with your Cloudinary credentials.<\/li>\n<\/ol>\n<p>This single call instructs Cloudinary to automatically track several key metrics: plays, watch time, demographic, and session completion.<\/p>\n<h3>Simplified Code Concept<\/h3>\n<p>The logic is handled without the need for manual event wiring or complex timers:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> analytics = connectCloudinaryAnalytics(videoElement);\n\nanalytics.startManualTracking({\n  cloudName,\n  publicId,\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Cloudinary manages heartbeats, session boundaries, and data aggregation. To ensure sessions close cleanly and all pending data is flushed, especially when swapping media or unmounting components, we\u2019ll call <code>stopManualTracking()<\/code>.<\/p>\n<p>This approach is compatible with React and Next.js, because it avoids double-tracking issues during component remounts. At this stage, Cloudinary collects real engagement data on their servers.<\/p>\n<h2>Sending Analytics Safely<\/h2>\n<p>Tracking is only half the job; you\u2019ll still need to read and interpret the data. Cloudinary\u2019s Video Analytics API uses basic authentication, requiring your API key and API secret.<\/p>\n<h3>Why We Use a Server Proxy<\/h3>\n<p>You never want these secrets in the browser; even if you attempt to hide them, they can leak. To maintain security, we\u2019ll fetch analytics from the server using a <strong>Next.js API route<\/strong> that acts as a safe proxy.<\/p>\n<p>The communication flow works like this:<\/p>\n<ol>\n<li>The client calls <code>\/api\/analytics?publicId=...<\/code>.<\/li>\n<li>The server constructs a Base64-encoded <code>&lt;api_key&gt;:&lt;api_secret&gt;<\/code> string for the <code>Authorization<\/code> header.<\/li>\n<li>The server calls Cloudinary\u2019s analytics endpoint: <code>\/video\/analytics\/views?expression=...<\/code>.<\/li>\n<\/ol>\n<p>This architecture keeps your credentials server-only while allowing you to control timeouts, retries, and caching.<\/p>\n<h3>Secure Implementation<\/h3>\n<p>The main logic follows this secure pattern:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> url = \n  <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-subst\">${cloudName}<\/span>\/video\/analytics\/views`<\/span> + \n  <span class=\"hljs-string\">`?expression=<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(<span class=\"hljs-string\">`video_public_id=<span class=\"hljs-subst\">${publicId}<\/span>`<\/span>)}<\/span>`<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(url, {\n  <span class=\"hljs-attr\">headers<\/span>: { <span class=\"hljs-attr\">Authorization<\/span>: authHeader },\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Handling Network Realities<\/h3>\n<p>Real networks fail, so we\u2019ll add robust guardrails to handle:<\/p>\n<ul>\n<li>\n<strong>Missing environment variables.<\/strong> Ensuring the API doesn\u2019t run without proper configuration.<\/li>\n<li>\n<strong>Slow connections and random timeouts.<\/strong> Implementing manual timeout signals to prevent the server from hanging.<\/li>\n<li>\n<strong>Flaky outbound routes.<\/strong> Adding retries with backoff and keep-alive sockets to reduce failures, specifically for environments like Vercel where connection timeouts can occur.<\/li>\n<\/ul>\n<p>The full server route lives here in the repo: <a href=\"https:\/\/github.com\/musebe\/video-insights-demo\/blob\/main\/app\/api\/analytics\/route.ts\">https:\/\/github.com\/musebe\/video-insights-demo\/blob\/main\/app\/api\/analytics\/route.ts<\/a>.<\/p>\n<blockquote>\n<p>You can also find the full API documentation here: <a href=\"https:\/\/cloudinary.com\/documentation\/video_analytics\">https:\/\/cloudinary.com\/documentation\/video_analytics<\/a>.<\/p>\n<\/blockquote>\n<h2>Fetching and Normalizing Data<\/h2>\n<p>Cloudinary returns analytics as raw \u201cview rows\u201d through its programmatic API. Each row represents an individual viewing session that has ended, provided in a structured <code>data<\/code> array.<\/p>\n<h3>Understanding the Raw Response<\/h3>\n<p>The API response includes several critical fields that we use to build our creator insights:<\/p>\n<ul>\n<li>\n<strong><code>view_watch_time<\/code>.<\/strong> The total seconds a viewer spent on the video.<\/li>\n<li>\n<strong><code>viewer_location_country_code<\/code>.<\/strong> The ISO country code of the viewer.<\/li>\n<li>\n<strong><code>viewer_application_name<\/code><\/strong>. The browser or app used (e.g., Chrome, Safari).<\/li>\n<li>\n<strong><code>viewer_os_identifier<\/code><\/strong>. Detailed operating system information.<\/li>\n<li>\n<strong><code>view_ended_at<\/code><\/strong>. The precise timestamp when the session finished.<\/li>\n<\/ul>\n<h3>The Normalization Process<\/h3>\n<p>Raw data can often be \u201cnoisy.\u201d Sometimes we\u2019ll see duplicates or sessions with a <code>watch_time<\/code> of zero, which occurs when a session starts but ends immediately or during rapid player testing. To ensure the dashboard tells an accurate story, we\u2019ll perform three key normalization steps:<\/p>\n<ol>\n<li>\n<strong>Sort by time.<\/strong> We sort data by <code>view_ended_at<\/code> so the most recent interactions appear first, keeping the dashboard feeling \u201clive\u201d.<\/li>\n<li>\n<strong>Deduplicate sessions.<\/strong> We\u2019ll use a stable key by combining <strong>Public ID<\/strong>, <strong>End Time<\/strong>, <strong>Browser<\/strong>, <strong>OS<\/strong>, and <strong>Country<\/strong>, to identify duplicate heartbeats. If duplicates are found, we\u2019ll retain the row with the higher watch time to ensure we don\u2019t under-report engagement.<\/li>\n<li>\n<strong>Filter noise.<\/strong> While we keep most sessions, we can filter out extremely short bursts (zero-second views) if they clutter the visualization without adding value to the creator\u2019s strategy.<\/li>\n<\/ol>\n<h3>Simplified Cleaning Logic<\/h3>\n<p>The core logic utilizes a <code>Map<\/code> to deduplicate and then converts it back to a sorted array:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> map = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Map<\/span>&lt;string, CloudinaryView&gt;();\n\n<span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> v <span class=\"hljs-keyword\">of<\/span> rows) {\n  <span class=\"hljs-keyword\">const<\/span> key = &#91;\n    v.video_public_id,\n    v.view_ended_at,\n    v.viewer_application_name,\n    v.viewer_location_country_code,\n    v.viewer_os_identifier,\n  ].join(<span class=\"hljs-string\">\"|\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> prev = map.get(key);\n\n  <span class=\"hljs-comment\">\/\/ Keep the session with the most engagement data<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (!prev || v.view_watch_time &gt; prev.view_watch_time) {\n    map.set(key, v);\n  }\n}\n\n<span class=\"hljs-keyword\">const<\/span> cleaned = <span class=\"hljs-built_in\">Array<\/span>.from(map.values()).sort(\n  <span class=\"hljs-function\">(<span class=\"hljs-params\">a, b<\/span>) =&gt;<\/span> +<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(b.view_ended_at) - +<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(a.view_ended_at)\n);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>To keep the UI responsive without overloading the API, we\u2019ll use careful polling that avoids overlapping requests or fast refresh loops. The full implementation of this logic can be explored in our <a href=\"https:\/\/github.com\/musebe\/video-insights-demo\/blob\/main\/app\/dashboard\/page.tsx\">dashboard source code<\/a>.<\/p>\n<h2>Building the Dashboard<\/h2>\n<p>Once the data is clean, the UI becomes simple. We\u2019ll only show what helps a creator decide what to do next. The dashboard is organized into two layers.<\/p>\n<h3>Layer 1: High-Level Signals<\/h3>\n<p>The first layer provides quick signals numbers that can be checked in seconds to gauge performance:<\/p>\n<ul>\n<li>\n<strong>Total views<\/strong> represents the total number of view sessions received, calculated directly from <code>data.length<\/code>.<\/li>\n<li>\n<strong>Average watch time<\/strong> serves as a fast proxy for retention by averaging <code>view_watch_time<\/code>.<\/li>\n<li>\n<strong>Countries<\/strong>: Identifies where viewers are located by mapping unique <code>viewer_location_country_code<\/code> values.<\/li>\n<\/ul>\n<h3>Layer 2: The Engagement Story<\/h3>\n<p>The second layer reveals the story over time using an engagement timeline built with Recharts. Each point on the graph represents a single view session where the value is the <code>view_watch_time<\/code>.<\/p>\n<p>To keep the UI focused, we\u2019ll maintain a chart design with a minimal UI, but clean visualization to highlight engagement patterns and detailed tooltips that display the session timestamp.<\/p>\n<p>The data mapping for the chart is straightforward:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> series = cleaned\n  .slice()\n  .reverse()\n  .map(<span class=\"hljs-function\">(<span class=\"hljs-params\">v, i<\/span>) =&gt;<\/span> ({\n    <span class=\"hljs-attr\">n<\/span>: i,\n    <span class=\"hljs-attr\">s<\/span>: v.view_watch_time || <span class=\"hljs-number\">0<\/span>,\n    <span class=\"hljs-attr\">t<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(v.view_ended_at).toLocaleString(),\n  }));\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>If watch time drops after a content change, or if a new \u201chook\u201d successfully retains viewers, the trend is easy to read. Finally, we\u2019ll include an \u201cInsight Card\u201d. It provides a friendly nudge based on the data:<\/p>\n<ul>\n<li>\n<strong>Drop-off warning.<\/strong> If <code>avgWatch &lt; 30<\/code>, it warns the creator about early drop-offs.<\/li>\n<li>\n<strong>Engagement signal.<\/strong> If the average is higher, it signals strong content resonance.<\/li>\n<\/ul>\n<blockquote>\n<p>You can review the full dashboard implementation here: <a href=\"https:\/\/github.com\/musebe\/video-insights-demo\/blob\/main\/app\/dashboard\/page.tsx\">https:\/\/github.com\/musebe\/video-insights-demo\/blob\/main\/app\/dashboard\/page.tsx<\/a>.<\/p>\n<\/blockquote>\n<h2>Lessons Learned and Next Steps<\/h2>\n<ul>\n<li>\n<strong>Real networks are messy.<\/strong> Production environments like Vercel can encounter outbound connection timeouts. We solved this by implementing <strong>AbortController<\/strong> signals and retries to ensure the dashboard remains stable even during network hiccups.<\/li>\n<li>\n<strong>Data cleaning is mandatory.<\/strong> Raw streams often contain \u201cnoise\u201d from rapid testing or incomplete sessions. A small deduplication and sorting step prevents misleading vanity metrics and ensures the <strong>Average Watch Time<\/strong> represents real human engagement.<\/li>\n<li>\n<strong>Polling vs. SSE.<\/strong> Polling is a reliable starting point for near-real-time updates if managed with discipline (i.e., one request at a time every 15 to 30 seconds). For true \u201clive\u201d updates, <strong>Server-sent events (SSE)<\/strong> or WebSockets would be the natural next step.<\/li>\n<li>\n<strong>Simplicity wins.<\/strong> Creators don\u2019t want 10 complex charts; they want to know if their new \u201chook\u201d improved retention. Keeping the UI focused on drop-off points ensures the data is intuitive and actionable.<\/li>\n<\/ul>\n<p>Analytics are only useful when actionable. Cloudinary provides the rich, raw \u201cingredients,\u201d but it\u2019s up to the developer to shape them into actionable signals for the creator. <a href=\"https:\/\/cloudinary.com\/users\/register_free\">Sign up<\/a> for Cloudinary to try it yourself today.<\/p>\n<ul>\n<li>\n<strong>Live Demo<\/strong>: <a href=\"https:\/\/video-insights-demo.vercel.app\/\">https:\/\/video-insights-demo.vercel.app\/<\/a>\n<\/li>\n<li>\n<strong>Source Code<\/strong>: <a href=\"https:\/\/github.com\/musebe\/video-insights-demo\">https:\/\/github.com\/musebe\/video-insights-demo<\/a>\n<\/li>\n<\/ul>\n<h3>What to Build Next<\/h3>\n<p>If you want to extend this project, here are some high-impact upgrades:<\/p>\n<ul>\n<li>\n<strong>Pagination.<\/strong> Implement <code>next_cursor<\/code> logic to allow creators to load their full viewing history beyond the initial batch.<\/li>\n<li>\n<strong>Advanced filtering.<\/strong> Add filters by <strong>Country<\/strong>, <strong>OS<\/strong>, or <strong>Browser<\/strong> to identify if content performs differently across demographics.<\/li>\n<li>\n<strong>Drop-off bucketing.<\/strong> Categorize views into \u201cBuckets\u201d (e.g., 0\u20133s, 3\u201310s, 30s+) to visualize exactly where the \u201cleaky bucket\u201d is in your content funnel.<\/li>\n<li>\n<strong>A\/B testing with <code>customData<\/code>.<\/strong> Use Cloudinary\u2019s <code>customData<\/code> tags to track two different versions of a video and see which one retains viewers longer.<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":87,"featured_media":39758,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[212,303],"class_list":["post-39757","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-next-js","tag-video"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Stop Guessing, Start Growing With Cloudinary Video Analytics<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Stop Guessing, Start Growing With Cloudinary Video Analytics\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-01-27T15:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-27T23:19:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Stop Guessing, Start Growing With Cloudinary Video Analytics\",\"datePublished\":\"2026-01-27T15:00:00+00:00\",\"dateModified\":\"2026-01-27T23:19:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA\",\"keywords\":[\"Next.js\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2026\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\",\"url\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\",\"name\":\"Stop Guessing, Start Growing With Cloudinary Video Analytics\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA\",\"datePublished\":\"2026-01-27T15:00:00+00:00\",\"dateModified\":\"2026-01-27T23:19:34+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Stop Guessing, Start Growing With Cloudinary Video Analytics\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Stop Guessing, Start Growing With Cloudinary Video Analytics","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics","og_locale":"en_US","og_type":"article","og_title":"Stop Guessing, Start Growing With Cloudinary Video Analytics","og_url":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics","og_site_name":"Cloudinary Blog","article_published_time":"2026-01-27T15:00:00+00:00","article_modified_time":"2026-01-27T23:19:34+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","type":"image\/png"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Stop Guessing, Start Growing With Cloudinary Video Analytics","datePublished":"2026-01-27T15:00:00+00:00","dateModified":"2026-01-27T23:19:34+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","keywords":["Next.js","Video"],"inLanguage":"en-US","copyrightYear":"2026","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics","url":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics","name":"Stop Guessing, Start Growing With Cloudinary Video Analytics","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","datePublished":"2026-01-27T15:00:00+00:00","dateModified":"2026-01-27T23:19:34+00:00","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/start-growing-video-analytics"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/start-growing-video-analytics#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Stop Guessing, Start Growing With Cloudinary Video Analytics"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1769554859\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6\/StartGrowingwithCloudinaryVideoAnalytics-blog-1j4O6.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=39757"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39757\/revisions"}],"predecessor-version":[{"id":39759,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/39757\/revisions\/39759"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/39758"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=39757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=39757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=39757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}