Skip to content

RESOURCES / BLOG

How I Rebuilt Our Video Infrastructure Using Cloudinary Video API

Why It Matters

  • The original video infrastructure was a disconnected system of manual processes, leading to operational bottlenecks and inconsistent user experience.
  • Migrating to Cloudinary’s Video API resulted in video loading times decreased by an average of 60% and average video start time dropped from 3.2s to 0.8s.
  • The rebuild required a four-phase approach: upload API integration, video delivery optimization, video player implementation, and advanced API features.

Before switching to Cloudinary’s Video API, the infrastructure I used worked to some extent, but it required a lot of manual work, processing and encoding videos, and getting them to load as fast as possible.

Moving to Cloudinary improved my performance metrics significantly and eliminated the operational overhead of managing multiple video processing systems. In numbers:

  • Video loading times decreased by 60% on average.
  • The average video start time went from 3.2s to 0.8s.
  • Jumping from one part of a video to another became almost instant.

In this blog post, we’ll go over a full technical breakdown of how the migration worked. We’ll start with the original video infrastructure I was using and its challenges, before I walk you through the migration to the Cloudinary Video API. Finally, we’ll go over the performance results I got from that migration and how you can replicate them. 

Like a lot of modern web projects, this one was put together with a variety of tools and systems. Without going into too many specifics, a significant part of that project was centered around video. If videos didn’t load timely or users ran into regular errors during playback, it could ruin the whole experience. 

The original video “stack” I used consisted of multiple disconnected systems that created operational bottlenecks and performance issues. At one point, I realized that most of the errors I was running into were related to video playback, hence the need to find a new stack, or, in the case of Cloudinary, a one-stop solution.

Here are the problems I encountered using my old video infrastructure.

  • Video processing and encoding problems. My video processing workflow relied on custom FFmpeg scripts that required constant maintenance and monitoring. Every time I needed to support a new video format or adjust quality settings, I had to manually update encoding configurations and test them across different input types. The TL’DR: I was spending significant time troubleshooting encoding failures, manually reprocessing videos, and ensuring consistent quality across different resolutions.
  • Mobile video optimization challenges.Mobile video delivery was a constant source of problems. I had to create different encoding profiles for various devices manually, manage adaptive bitrate streaming logic, and handle network condition changes.
  • Limited video analytics and monitoring.Beyond basic server logs, I had zero insight into video performance metrics like buffering rates, playback success, or user engagement patterns. That’s a big fail for a modern web project since, without analytics, you’re essentially flying blind. 

The stack “worked” to some extent. Videos loaded, albeit sometimes slower than expected, and I’d run into the occasional error depending on what type of device I accessed them from. However, there was room for improvement, which is where the Cloudinary Video API came in.

I evaluated several video infrastructure providers before choosing Cloudinary’s API-first approach. Using Cloudinary’s API, I was able to automate video processing and encoding, as well as optimize files for mobile delivery, all with scalable code. Other platforms offer similar functionality, but integrating Cloudinary into my workflow was a straightforward process, thanks to great documentation.

Here’s how I implemented the migration across four phases.

Note:

If you don’t want to deal with the API directly, Cloudinary also enables you to set up complex video transformation and optimization workflows using MediaFlows.

The first step was replacing my FFmpeg processing with Cloudinary’s upload API. Using Cloudinary’s Node.js SDK, I replaced my FFmpeg processing with a single API call:

// New approach: Single API call handles all transformations

const result = await cloudinary.uploader.upload(videoFile, {

  resource_type: "video",

  eager: [

    { width: 1920, height: 1080, crop: "limit", quality: "auto" },

    { width: 1280, height: 720, crop: "limit", quality: "auto" },

    { width: 854, height: 480, crop: "limit", quality: "auto" }

  ]

});Code language: JavaScript (javascript)

One important consideration: Cloudinary processes transformations asynchronously. When processing completes, it sends an HTTP callback (webhook) to notify my application that the video is ready, so I needed to set up an endpoint to receive these notifications.

Cloudinary’s delivery system can automatically serve the optimal format based on which browser you’re using. That means a lot less time spent optimizing video for delivery across different devices or use cases. With the Cloudinary API, you can use the fetch_format (or f_auto) parameter to transcode video files on demand and automatically fetch the best option for each use case:

// Cloudinary Node.js SDK - Automatic format and quality optimization

const videoUrl = cloudinary.url('sample-video', {

  resource_type: 'video',

  fetch_format: 'auto',

  quality: 'auto'

});Code language: JavaScript (javascript)

I created a URL migration strategy to gradually transition existing embedded videos without breaking current implementations. This can be time-consuming if you’re trying to migrate and optimize an existing library of content. However, it’s definitely worth it if you’re struggling with video performance.

Replacing the video player I was using with Cloudinary’s fully customizable Video Player required mapping my existing event tracking to the new system:

// Mapping legacy analytics events to new player

player.on('play', (event) => {

  analytics.track('video_play', {

    video_id: event.publicId,

    duration: event.duration,

    quality: event.videoHeight

  });

});Code language: PHP (php)

The transition period required running both tracking systems in parallel to ensure data accuracy. I gradually phased out the old HTML5 events once I verified that the new player events were providing consistent data.

Right out of the gate, the Cloudinary Video Player delivered a better, more user-friendly experience, including several accessibility features, like being able to easily set up captions:

The Cloudinary Video Player

With the new implementation in place, it was time to take a look at some of Cloudinary’s more advanced video optimization functionality. Namely, adaptive streaming and video analytics, which were sorely missing from my initial implementation.

// Cloudinary Video Player with adaptive streaming

const player = cloudinary.videoPlayer('player', {

  publicId: 'sample-video',

  adaptive: true,

  fluid: true,

  controls: true

});

// Access to detailed video analytics via Node.js SDK

const analytics = await cloudinary.api.usage({

  resource_type: 'video',

  granularity: 'daily'

});Code language: JavaScript (javascript)

The analytics API provided insights I never had before, including a detailed breakdown of bandwidth usage, transformation counts, and regional delivery performance. I could now see which video qualities were being served most frequently and optimize my encoding profiles accordingly.

Previous architecture:

  • Multiple FFmpeg servers with inconsistent configurations and no orchestration.
  • Manual mobile optimization with device-specific encoding profiles.
  • No support for analytics.

Current architecture:

  • Single Video API for upload and transformation.
  • Cloudinary’s global CDN with automatic optimization and failover.
  • Automatic mobile optimization with adaptive streaming.
  • Comprehensive analytics and performance monitoring.

Overall, migrating from my previous video stack to Cloudinary was straightforward process. Using the API for video management helped me consolidate workflows since you can easily reuse code for most of your videos, at least for simple projects.

After completing the video stack migration, I discovered additional Cloudinary optimizations that I hadn’t initially considered but that could be valuable for your setup. Here’s what I overlooked during the original migration

Some older browsers didn’t support automatic format selection. I implemented fallback logic for edge cases. In this case, if the browser doesn’t support WebM, Cloudinary transcodes the file to MP4:

// Cloudinary Node.js SDK - Fallback for browsers with limited format support

if (!player.canPlayType('video/webm')) {

  videoUrl = cloudinary.url('sample-video', {

    resource_type: 'video',

    fetch_format: 'mp4'

  });

}Code language: JavaScript (javascript)

As I mentioned, these are edge cases. However, you’d be surprised at how many people are still rocking old browsers like Internet Explorer (even though it’s no longer supported). These cases were difficult to test for, but there are services that make it possible. BrowserStack, for example, lets you test web applications across a broad range of browsers, including outdated options.

Cloudinary sends webhook notifications when video transformations are complete. However, webhooks can occasionally fail due to network issues or server downtime. When this happens, your application continues waiting for a transformation that’s actually finished.

I implemented a polling fallback to check transformation status directly via API when webhooks don’t arrive:

// Using Cloudinary Node.js SDK, polls the API to check if a video transformation has completed

const pollForCompletion = async (publicId) => {

  let attempts = 0;

  // Try up to 20 times before timing out

  while (attempts < 20) {

    // Fetch the current status of the video resource

    const resource = await cloudinary.api.resource(publicId, {

      resource_type: 'video'

    });

    // If transformation is complete, return the resource data

    if (resource.status === 'complete') return resource;

    // Wait 5 seconds before polling again

    await sleep(5000);

    attempts++;

  }

  // After 20 failed attempts, throw a timeout error

  throw new Error('Video processing timeout');

};Code language: JavaScript (javascript)

After completing the migration, I measured significant improvements across multiple metrics:

The most significant improvements were in video delivery performance. By leveraging Cloudinary’s automatic optimizations and global CDN, I measured substantial improvements across key metrics that directly impact user experience.

On average, video load times decreased by 60% through automatic format optimization. Here’s a quick example of how fast a test video file (approximately 13MB) loads using Cloudinary to serve the content:

Using the Cloudinary Video Player to serve content

These performance gains were possible because Cloudinary automatically serves the optimal video format and quality for each user’s device and network conditions, while its global CDN ensures faster delivery through geographically distributed edge servers.

Beyond performance improvements, the migration significantly reduced the operational burden of managing video infrastructure. The shift from manual processes to API-driven automation freed up considerable development time. Here are the improvements by the numbers:

  • Video processing automation eliminated a good portion of manual work (optimizing videos manually, selecting the best formats for each use case, setting up complex fallbacks, and more).
  • Deployment complexity was reduced significantly by removing CDN configuration management, since Cloudinary uses its own CDN.
  • Cross-browser compatibility issues were eliminated almost completely since Cloudinary can select the best video format for each use case.

Instead of spending time troubleshooting encoding failures and managing server configurations, I could focus on building features that improved the user experience. It’s a win-win for me, as I spend less time on optimization, and for users, since loading and reproducing videos is now smoother.

The infrastructure changes had a direct impact on how visitors interacted with video content on the site. Here are the actual numbers:

  • Average video start time improved from 3.2 seconds to 0.8 seconds.
  • Mobile playback issues decreased significantly with the responsive Cloudinary player (which I tested using BrowserStack).
  • It became faster to jump from one part of the video to the next.

To put those improvements into context, I put together a test with identical videos side-by-side. The video on the left is the unoptimized version, whereas the one to the right is served using the Cloudinary API:

A side-by-side comparison using unoptimized video and the Cloudinary Video API

Slow-loading videos and playback errors can be very frustrating to deal with as a user. Being able to reduce them means dealing with fewer support requests and users ditching the app for an alternative.

If you want a simple checklist you can reference while migrating to the Cloudinary Video API from a different solution, here it is. This checklist follows the processes I’ve outlined throughout the article and links you to the corresponding documentation you may need to review. 

  • Audit existing video URLs and create migration strategy.
  • Map current player events to new video API system.
  • Set up webhook handling with polling fallback.
  • Plan gradual rollout rather than complete system replacement.
  • Test automatic format selection with actual user base (or web app testing tools).
  • Set up monitoring for video transformation completion.
  • Go through the Cloudinary Video API documentation to prepare for implementation.
  • Set up staging environments matching production configuration.
  • Plan support for parallel old and new systems during the transition (if you have an existing user base).

Based on my experience migrating to the Cloudinary Video API, I recommend that you start with new uploads and monitor everything from day one. I routed new videos through Cloudinary while keeping existing content on the legacy system, which let me validate performance without breaking current functionality. The analytics insights from Cloudinary were immediately valuable for understanding video delivery patterns while completing the rebuilding process.

Aside from that, allocate extra time for edge cases and plan a gradual transition. Every video setup has unique quirks that take time to resolve. Running both systems in parallel eliminated user-facing issues while I worked through compatibility challenges. Overall, the operational time savings and performance improvements more than made up for the time it took me to set up the new video infrastructure using Cloudinary.

If you’re facing similar scaling or performance issues with video, Cloudinary’s programmable video workflows are worth exploring. Sign up on Cloudinary to start building your own programmable video workflows.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free