Upload multiple videos via URL


Please use the v2 Management API to upload your content.

The v1 Management API will be deprecated in June 2023. Learn more about the v2 Management API benefits and migration steps.

In some instances, you may want to programmatically add videos to your JW Player library by fetching videos from a URL. This article explains how to implement this process.


JW Player licenseIf you need a license, click the following badge:

Property Key and Secret1. From your API Credentials page, scroll down to the Property Name section.
2. Click SHOW CREDENTIALS to reveal the Key and Secret for a property.
3. Copy the Key and Secret.

If you have more than one property in your account, be sure to click SHOW CREDENTIALS for the property you want to query. Each property has a unique set of credentials.

Treat your credentials like a password. However, if your credentials do become compromised Submit a Support Case or contact your JW Player representative.
JW Player client libraryThe Node.js, PHP, and Python libraries are used as examples in this article.

.csv fileFile containing videos to upload to your JW Player account.

Read the following section to learn about the structure of the .csv file.

The .csv file structure

The .csv file is a list of videos with relevant video metadata that you want to add to your JW Player library. When creating your .csv file, we highly recommend including download_url, title, description, tags, date, and link as the first six columns in the header. You can include additional header columns to include custom video metadata.


Recommended and custom column headers

Column definitions

The following table defines each column that can be included in a properly formatted .csv file.


Special characters should not be HTML-encoded. A number of publishers and services use HTML-encoded characters within their video metadata. HTML-encoded characters in the title, description, and tags columns should be decoded before pushing to the API.

download_url*(Required) Remote URL of your video files

Be sure that your videos adhere to the guidelines here to ensure our platform can transcode them.
custom.paramUser-defined parameter

The param part of the column header -- after the '.' separator -- specifies the name of the parameter. A parameter name should adhere to the following naming rules:

   • Can contain letters, numbers, periods (.), underscores (_), and hyphens (-)
   • Cannot start with a number or punctuation character
   • Cannot contain spaces
   • Cannot use reserved names, for example: file, tracks, sources, feedid, mediaid
   • Cannot use any existing non-custom parameter names, for example: title, description, author
dateVideo publish date in YYYY-MM-DD format

This specifies the date and time when the video should be available for streaming.
descriptionDescription of the video
linkURL of the web page where this video is published
tagsTags for the video

Multiple tags should be comma-separated within quotation marks.
titleTitle of the video
http://video.my_platform.net/2324731/5288693/16179765?signature=f00d4fae14055dd97c35987c7c1e4aae30865cb8,This is Title One,This is Description One,tag1,2012-08-25,http://www.my_site/page1.html,5288693,http://images.my_platform.net/archive/images/10055001.jpg
http://video.my_platform.net/2324731/5288693/16179766?signature=f00d4fae14055dd97c35987c7c1e4aae30865cb9,This is Title Two,This is Description Two,"tag1,tag2",2013-08-19,http://www.my_site/page2.html,5287268,http://images.my_platform.net/archive/images/10055002.jpg
http://video.my_platform.net/2324731/5288693/16179767?signature=f00d4fae14055dd97c35987c7c1e4aae30865cb0,This is Title Three,This is Description Three,tag2,2014-06-19,http://www.my_site/page3.html,5287267,http://images.my_platform.net/archive/images/10055003.jpg
http://video.my_platform.net/2324731/5288693/16179768?signature=f00d4fae14055dd97c35987c7c1e4aae30865cb1,This is Title Four,This is Description Four,"tag1,tag3",2014-08-19,http://www.my_site/page4.html,5287266,http://images.my_platform.net/archive/images/10055004.jpg

Process the .csv file

After using the previous section to create your .csv file, use the following steps to programmatically process the data within the file:

  1. Add the Node.js, PHP, or Python client library to your codebase.
  2. Configure the appropriate batch upload code below.

    This batch upload code performs the following tasks:
       • Imports the client library
       • Parses the .csv file into an array or list
       • Loops over each row to create a video record
       • (Optional) Outputs the video_key created for each video record
const csv = require('csv-parser');
const fs = require('fs');
const JWPlatformAPI = require('jwplatform');
const moment = require('moment');
const jwApiInstance = new JWPlatformAPI({
    apiKey: API_KEY,
    apiSecret: API_SECRET,
const csvPath = 'PATH_TO_CSV.csv';
const videos = [];
    .on('data', (row) => {
        const { download_url, title, tags, date } = row;
        const dateObj = new Date(date);
        const unixTimestamp = moment(dateObj).unix();
            date: unixTimestamp,
            'custom.legacy_id': row['custom.legacy_id'],
            'custom.thumbnail_url': row['custom.thumbnail_url'],
    .on('end', () => {
        videos.forEach((video) => {
$jwplatform = new Jwplayer\JwplatformAPI('INSERT API KEY', 'INSERT API SECRET');
// you can hardcode a file path
$csv_filepath = 'PATH_TO_YOUR_CSV.csv'; 
// or upload a CSV through a form 
// $csv_filepath = $_FILES['file']['tmp_name'];
if(!empty($csv_filepath) && file_exists($csv_filepath)) {
    $csv_rows = array_map('str_getcsv', file($csv_filepath));    
    $fields = array_shift($csv_rows); //parse the header line
    foreach($csv_rows AS $idx => $video) {   
        $video_meta = array_combine($fields, $video);                
        $upload_params = array();        
        if(!empty($video_meta['download_url'])) $upload_params['download_url'] = $video_meta['download_url'];
        if(!empty($video_meta['title'])) $upload_params['title']               = html_entity_decode($video_meta['title']);
        if(!empty($video_meta['tags'])) $upload_params['tags']                 = html_entity_decode($video_meta['tags']);
        if(!empty($video_meta['description'])) $upload_params['description']   = html_entity_decode($video_meta['description']);                    
        if(!empty($video_meta['custom'])) $upload_params['custom']             = html_entity_decode($video_meta['custom']);
        if(!empty($video_meta['date'])) $upload_params['date']                 = is_int($video_meta['date']) ? $video_meta['date'] : strtotime($video_meta['date']);
        foreach($video_meta AS $field => $val) {            
            if(strpos($field, 'custom.') !== FALSE) {
                $upload_params[$field] = $val;
        $response = $jwplatform->call("/videos/create", $upload_params);
        if($response['status'] === 'ok') {
            echo $upload_params['title'] . " upload success. key = " . $response['video']['key'] . "\n";
        else {
            echo $upload_params['title'] . " upload failed.\n"; 
import time
import html
import csv

import jwplatform.v1

client = jwplatform.v1.Client("API_KEY", "API_SECRET")

def needs_unescaping(key):
    return key in ["title", "description", "tags"] or key.startswith("custom.")

def parse_row(row):
    video = {
        k: html.unescape(v) if needs_unescaping(k) else v for k, v in row.items() if v

    if "date" in video and not video["date"].isdigit():
        video["date"] = int(time.mktime(time.strptime(video["date"], "%Y-%m-%d")))

    return video

if __name__ == "__main__":
    with open("test.csv") as csvfile:
        reader = csv.reader(csvfile)
        headings = next(reader)
        for row in reader:
            row = dict(zip(headings, row))
            video = parse_row(row)

                result = client.videos.create(**video)
            except jwplatform.errors.JWPlatformError as e:
                print("{} upload failed ({})".format(video["title"], e.message))

            if result["status"] == "ok":
                    "{} upload success: key = {}.".format(
                        video["title"], result["video"]["key"]
                print("{} upload failed ({})".format(video["title"], result["message"]))

The following is a successful response to a batch upload using PHP.

$ php batch.php
Sintel Trailer upload success. key = aqs7u6xe
Big Buck Bunny Trailer upload success. key = cx8nts7l
Elephants Dream Trailer upload success. key = ad98vs7d

Now that your videos have been fetched, you can protect them with signed URLs, add them to a web player, or incorporate them into a mobile app.