Tutorials · 8 min read

Node.js and api.video

Building a video upload demo with Node.js and api.video

Using our APIs (and our SDKs) are a straightforward and easy way to add video to your service.

Doug Sillars

February 1, 2022

Here at api.video, we love making video delivery easy for our customers. We take care of doing all the video transcoding and video streaming so that you can focus on all the other facets of your product. Using our APIs (and our SDKs) is a straightforward and easy way to add video to your service. In this post, we’ll walk through the steps to create a website where you can upload videos.

Note: Use NodeJS in the middle results in a "double upload" - the video is uploaded to the Node server - then to api.video. You can avoid this with a delegated token and our JS library.

You can also check out our API reference documentation here: API Reference documentation

Overall Architecture

Overall architecture api.video

  1. Starting with a web form, a video is submitted to the Node server.
  2. The video is immediately uploaded to api.video for processing
  3. api.video acknowledges the video upload, returns the videoId, and begins encoding the video.
  4. The node server polls api.video to obtain the playable status (confirming that encoding is completed).
  5. As long as the result is “false”, the polling continues.
  6. When the video is playable, the Node server returns a page with the video. The video autoplays to show that the video is ready to play. The page also returns the timings for upload & for video processing.
  7. The video link is e-mailed to the user (optional).

Getting Started

Node & Dependencies

There are a number of required Node dependencies:

npm install express dotenv @api.video/nodejs-sdk fs pug serve-favicon formidable email-validator

This tutorial also optionally connects to Intercom to send emails to your users. This will require:

npm install intercom-client

Building the Site

Starting index.js

You can clone the GitHub repository to follow along, but here is the code that builds your video uploader:

In the src/index.js file, copy the following code:


In this snippet, Node imports Express (and dotenv for environmental variables). The first big chunk of code creates and sets up an express app. It will use Pug to build the views. Static files are saved in the ‘public/’ directory, and serve-favicon adds the favicon to the site. You can use the default favicon in the public directory on GitHub, or create your own.

Next we have app.get — on the default entry point of the site. If a request occurs at the “/upload-demo” directory of the web server, we’ll deliver the start.pug file that is saved in the views directory.

Finally, the app listens on port 3000 for requests. If you run npm start from the terminal, you should get a response of “Example app listening on port 3000!” You can now enter “localhost:3000/upload-demo” into your browser, and if you have added the views directory — you’ll see the form. If you have not yet added the view, you’ll probably get an error.


The landing page is simply an introductory text describing the upload and a form to select the file. This page is created with Pug. Pug is really persnickety about indentation spaces, so it might be best to copy the code (or just use the code) from GitHub rather than copy and paste from this post.

In the head tag, we load some CSS and the favicon.


test api.video

The body loads descriptive text on api.video, and the submission form. The form takes 4 inputs: video name, e-mail, true/false mp4 support and the video file. You may notice that the form action will reload the root directory of the server. Since the data submission is a POST, it will be easy to identify the 2nd request.

POST Form Processing

Add these additional lines to the index.js. At the top of the file, we need to add dependencies:


api.video is our backend where uploaded videos are transcoded into video streams. We add the SDK, and then I store my API key in a .env file. I have added the .env file to my .gitignore file so that when I post my code on GitHub, my key is not uploaded and remains secure.

We also add formidable to read the form data and save the video locally on the Node server. The e-mail validator does what it sounds like it might do, and finally I add three variables to measure how quickly the service handles the video processing.

In the body of index.js, add the following:


We are now building the POST response. Formidable saves the video in the directory specified in the .env variable videoDir (this doesn’t have to be private, but it is nice to save all configurable variables in one place). When the upload requests mp4 support, the variable must be a boolean, so we use the string from the response to create the corresponding boolean value. The email validator validates the email, and if the email is valid  -  we use production credentials at api.video (permanent video hosting). If there is no e-mail, or it is not valid, the video is instead processed in the Sandbox, meaning that it will be watermarked & deleted after 24 hours. For your use — you probably just want to post to production, so you can simplify this logic.

Finally, we begin the upload of the video to api.video (and capture the time of the upload start).

If you try to run this code now, it will hang — we are not yet listening for the response from api.video. However, adding:


Will show the JSON response from api.video:


The videoID is important to display and playback the video, as are the assets. We also can see useful metadata about the video — Mp4Support, Panoramic, public/private, etc. Note that there is no mp4 url — the video must be encoded to receive that url, so we’ll grab it later.

Video Upload Response

After the “let result = client.videos.upload(files.source.path, {title: fields.title, mp4Support: mp4Support});” paste in the lines below.

We listen for the result from the upload. This allows us to mark upload completed (to measure the elapsed upload time). If the video has successfully been uploaded to api.video, we now have 2 copies of the video: one on the node server, and the other at api.video. Fs.unlink deletes the file on the node server. The JSON response gives us the videoId at api.video, and links to the iframe and player video urls (which we assign to variables). The mp4 url is only presented in this response when encoding is finished.

Even though we have the urls to the videos, they have not been processed yet. To determine if the video is playable, we call the videoStatus function. This sends a request to api.video to obtain the video status. The response is a JSON with lots of details about upload status, file size, and metadata like the dimensions and bitrate (you can learn more about these parameters in the documentation):


Reading the videoStats.encoding.playable value tells us if it is ready to play or not. If it is not ready, we call videoStatus again recursively until we get the affirmative response. When we receive that ‘playable’ is true (as in the example above), the function can then query for the MP4 url, and return the view to the user: return res.render(‘video’, {iframe, player, uploadSeconds,processSeconds});

This means that video.pug will be displayed to the end user with some variables (the urls of the video, and timings on upload and processing). We’ll discuss that view in the next section.


Displaying the Video

The Video.pug file is a simple html file that simply adds an iframe for the video, and reports the elapsed times for upload and processing of the video. The variables are ensconced in #{} tags. One other interesting thing to note is adding #autoplay to the url tells the video player to autoplay the video (you can also add #loop to loop the video).


And there we have it, a working video upload tool, where the response page shows the encoded video, along with the time for upload and for processing! Pretty cool!

webpage with video uploaded screenshot

Optional Step: Email integration

In the video upload response section, there is a line of code that calls the intercom integration:


We first create a lead (or a contact) inside Intercom. This will come back with an id that uniquely IDs the user. If the email address already exists, Intercom returns an error message that says “There is already a user with the id=.” So, if the user already exists, we can parse the error message to grab the user id.

Then we can add a tag to Intercom to show that the user has used the video upload tool, and we send an email to the user’s email address with links to the videos, and the time it took to upload and process the videos. The text of the email is right in the code, so easy to modify.

Update (2022)

We have a new version of a video uploader that uses a delegated token. It is live at upload.a.video, and you can read a post on delegated token upload. Delegated tokens remove the "double upload" to Node and then to api.video: videos are directly uploaded to api.video.


We’ve built a video uploader using Node.js and api.video. The easy to build tool now gives us the ability to easily share our videos across our internet properties. Using Node and api.video, the entire process creates an easy to use interface to take regular videos, and convert them into video streams. If this tutorial has inspired you (or if you have any questions), let us know by posting in our community forum.

Try out more than 80 features for free

Access all the features for as long as you need.
No commitment or credit card required

Video API, simplified

Fully customizable API to manage everything video. From encoding to delivery, in minutes.

Built for Speed

The fastest video encoding platform. Serve your users globally with 140+ points of presence. 

Let end-users upload videos

Finally, an API that allows your end-users to upload videos and start live streams in a few clicks.


Volume discounts and usage-based pricing to ensure you don’t exceed your budget.