Platform documentation & tutorials

Integrate Panda with your web app

API Documentation

This document outlines the RESTful interface Panda exposes to other applications. It helps you integrate Panda into your app or create libraries in other languages.

All requests should be sent to api.pandastream.com or api-eu.pandastream.com.

Videos

Video is a restfull resource representing the input video uploaded or downloaded to the system. It describes all major metadata of a video file, it’s size and it’s original filename. It contains a status attribute representing the uploading process of video to S3. status is set to success when the video has been successfully uploaded to S3. Carefull: It does not mean that it’s encodings have been encoded.

GET /videos.json

Optional parameters

status: one of 'success', 'fail', 'processing'. Filter by status

Example request

GET /videos.json

Example response

[{
  "id":"d891d9a45c698d587831466f236c6c6c",
  "original_filename":"test.mp4",
  "extname":".mp4",
  "path":"d891d9a45c698d587831466f236c6c6c",
  "video_codec":"h264",
  "audio_codec":"aac",
  "height":240,
  "width":300,
  "fps":29,
  "duration":14000,
  "file_size": 39458349,
  "created_at":"2009/10/13 19:11:26 +0100",
  "updated_at":"2009/10/13 19:11:26 +0100"
}, ... ]

GET /videos/:id.json

Example request

GET /videos/d891d9a45c698d587831466f236c6c6c.json

Example response

{
  "id":"d891d9a45c698d587831466f236c6c6c",
  "original_filename":"test.mp4",
  "extname":".mp4",
  "path":"d891d9a45c698d587831466f236c6c6c",
  "video_codec":"h264",
  "audio_codec":"aac",
  "height":240,
  "width":300,
  "fps":29,
  "duration":14000,
  "file_size":39458349,
  "created_at":"2009/10/13 19:11:26 +0100",
  "updated_at":"2009/10/13 19:11:26 +0100"
}

GET /videos/:id/encodings.json

Optional parameters

status: one of 'success', 'fail', 'processing'. Filter by status
profile_id: filter by profile_id
profile_name: filter by profile_name

Example request

GET /videos/d891d9a45c698d587831466f236c6c6c/encodings.json

Example response

[{
   "id":"2f8760b7e0d4c7dbe609b5872be9bc3b",
   "video_id":"d891d9a45c698d587831466f236c6c6c",
   "extname":".mp4",
   "path":"2f8760b7e0d4c7dbe609b5872be9bc3b"
   "profile_id":"40d9f8711d64aaa74f88462e9274f39a",
   "profile_name":"h264",
   "status":"success",
   "encoding_progress":99,
   "height":240,
   "width":300,
   "file_size":29458349,
   "started_encoding_at":"2009/10/13 21:28:45 +0000",
   "encoding_time":9000,
   "files":["2f8760b7e0d4c7dbe609b5872be9bc3b.mp4"],
   "created_at":"2009/10/13 20:58:29 +0000",
   "updated_at":"2009/10/13 21:30:34 +0000"
}, ... ]

GET /videos/:id/metadata.json

Example request

GET /videos/d891d9a45c698d587831466f236c6c6c/metadata.json

Example response

{
  "image_height"=>208,
  "audio_format"=>"mp4a",
  "selection_time"=>"0 s",
  "track_layer"=>0,
  "poster_time"=>"0 s",
  "video_frame_rate"=>30.0,
  "duration"=>"19.35 s",
  "media_create_date"=>Tue Jan 28 20:59:44 +0000 1913,
  "audio_sample_rate"=>48000,
  "compressor_id"=>"avc1",
  "graphics_mode"=>"srcCopy",
  "audio_channels"=>2,
  "media_header_version"=>0,
  "track_modify_date"=>Tue Jan 28 20:59:39 +0000 1913,
  "preferred_volume"=>"100.00%",
  "mime_type"=>"video/mp4",
  "file_size"=>"1435 kB",
  "create_date"=>Tue Jan 28 20:59:39 +0000 1913,
  ....
  "rotation"=>0
}

POST /videos.json

If you use Panda in your web application, you’ll probably want to use our Panda Uploader to upload video files from a web page. Otherwise, you can use this API to upload videos.

NOTE: when creating the authorisation signature for this request do not include the file parameter.

Required parameters

One of the following parameters is required:

file: the file that is being uploaded
source_url: url of a remote video file

If you use the file parameter, make sure that your HTTP request uses an appropriate Content-Type, such as multipart/form-data.

Optional parameters

profiles: comma-separated list of profile names or IDs to be used during encoding. Alternatively, specify "none" so no encodings are created yet
path_format: represents the complete video path without the extension name. It can be constructed using some provided keywords.

Path Format

The following keywords can be combined to create a path format.

:id         : Id of the video or the encoding.
:video_id   : Id of the video.
:original   : Original filename of the uploaded video
:date       : Date the video was uploaded. ('2009-10-09')
:profile    : Profile name used by the encoding ('original' is used when the file is the original video)
:type       : Video type, 'original' or 'encodings'
:resolution : Resolution of the video or the encoding. ('480x340')

e.g. “my-path/:video_id/:profile/:id”

By default path_format is set to “:id”

Example request

POST /videos.json

profiles: "h264"
path_format: "my-path/:id"
state_update_url: http://mypandasite.com/videos/update?id=$id

Example response

{
  "id":"d891d9a45c698d587831466f236c6c6c",
  "original_filename":"test.mp4",
  "extname":".mp4",
  "path":"my-path/d891d9a45c698d587831466f236c6c6c",
  "video_codec":"h264",
  "audio_codec":"aac",
  "height":240,
  "width":300,
  "fps":29,
  "duration":14000,
  "file_size": 29458349,
  "created_at":"2009/10/13 19:11:26 +0100",
  "updated_at":"2009/10/13 19:11:26 +0100"
}

DELETE /videos/:id.json

Example request

DELETE /videos/d891d9a45c698d587831466f236c6c6c.json

Example response

status: 200

Encodings

Each encoding represents a version of a video encoded with the settings defined in the profile used. When an encoding is created (either automatically when a video is uploaded, or using the POST method below) it is immediately placed on your encoding queue.

If the original video has been successfully uploaded to your s3 bucket, the video status will be set to ‘success’ If the video has failed, the encoding will fail as well.

If the encoding was successful, you can download it or play it using the following convention $path$$extname$. Both values can be retrieved using the API. As well, 7 screenshots will then be generated and uploaded to the S3 bucket with the name using the following convention: $path$_$screenshot_number$.jpg e.g. (my-path/2f8760b7e0d4c7dbe609b5872be9bc3b_1.jpg).

If the encoding fails, this is usually because there was either an invalid profile being used, or the origin video is corrupted or not recognised. In this case, a logfile will be uploaded to S3 (with the filename $path$.log) containing some debug information which can help in correcting issues caused by an invalid profile.

GET /encodings.json

Optional parameters

status: one of 'success', 'fail', 'processing'. Filter by status
profile_id: filter by profile_id
profile_name: filter by profile_name
video_id: filter by video_id

Example request

GET /encoding.json?status=success

Example response

[{
  "id":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "video_id":"d891d9a45c698d587831466f236c6c6c",
  "extname":".mp4",
  "path":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "profile_id":"40d9f8711d64aaa74f88462e9274f39a",.
  "profile_name":"h264",
  "status":"success",
  "encoding_progress":99,
  "height":240,
  "width":300,
  "started_encoding_at":"2009/10/13 21:28:45 +0000",
  "encoding_time":9000,
  "files":["2f8760b7e0d4c7dbe609b5872be9bc3b.mp4"],
  "created_at":"2009/10/13 20:58:29 +0000",
  "updated_at":"2009/10/13 21:30:34 +0000"
}, ... ]

GET /encodings/:id.json

Example request

GET /encodings/2f8760b7e0d4c7dbe609b5872be9bc3b.json

Example response

{
  "id":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "video_id":"d891d9a45c698d587831466f236c6c6c",
  "extname":".mp4",
  "path":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "profile_id":"40d9f8711d64aaa74f88462e9274f39a",
  "profile_name":"h264",
  "status":"success",
  "encoding_progress":99,
  "height":240,
  "width":300,
  "started_encoding_at":"2009/10/13 21:28:45 +0000",
  "encoding_time":9000,
  "files":["2f8760b7e0d4c7dbe609b5872be9bc3b.mp4"],
  "created_at":"2009/10/13 20:58:29 +0000",
  "updated_at":"2009/10/13 21:30:34 +0000"

}

POST /encodings.json

Create a new encoding for a video that already exists.

Required parameters

video_id: ID of existing video
profile_id: ID of existing profile
profile_name: Name of existing profile

Example request

POST /encodings.json

video_id: d891d9a45c698d587831466f236c6c6c
profile_name: h264

Example response

{
  "id":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "video_id":"d891d9a45c698d587831466f236c6c6c",
  "extname":".mp4",
  "path":"2f8760b7e0d4c7dbe609b5872be9bc3b",
  "profile_id":"40d9f8711d64aaa74f88462e9274f39a",
  "profile_name":"h264",
  "status":"processing",
  "encoding_progress":0,
  "height":240,
  "width":300,
  "started_encoding_at":"",
  "encoding_time":0,
  "files":[],
  "created_at":"2009/10/13 20:58:29 +0000",
  "updated_at":"2009/10/13 21:30:34 +0000"
}

POST /encodings/:id/cancel.json

Cancel an encoding

Example request

POST /encodings/2f8760b7e0d4c7dbe609b5872be9bc3b/cancel.json

Example response

status: 200

POST /encodings/:id/retry.json

Retry a failed encoding

Example request

POST /encodings/2f8760b7e0d4c7dbe609b5872be9bc3b/retry.json

Example response

status: 200

DELETE /encodings/:id.json

Example request

DELETE /encodings/2f8760b7e0d4c7dbe609b5872be9bc3b.json

Example response

status: 200

Profiles

GET /profiles.json

Example request

GET /profile.json

Example response

[{
   "id":"40d9f8711d64aaa74f88462e9274f39a",
   "title":"MP4 (H.264)",
   "name": "h264",
   "extname":".mp4",
   "width":320,
   "height":240,
   "audio_bitrate": 128,
   "video_bitrate": 500,
   "aspect_mode": "letterbox",
   "command":"ffmpeg -i $input_file$ -c:a libfaac $audio_bitrate$ -c:v libx264 $video_bitrate$ -preset medium $filters$ -y $output_file$",
   "created_at":"2009/10/14 18:36:30 +0000",
   "updated_at":"2009/10/14 19:38:42 +0000"
}, ... ]

GET /profiles/:id.json

Example request

GET /profiles/40d9f8711d64aaa74f88462e9274f39a.json

Example response

{
  "id":"40d9f8711d64aaa74f88462e9274f39a",
  "title":"MP4 (H.264)",
  "name": "h264",
  "extname":".mp4",
  "width":320,
  "height":240,
  "audio_bitrate": 128,
  "video_bitrate": 500,
  "aspect_mode": "letterbox",
  "command":"ffmpeg -i $input_file$ -c:a libfaac $audio_bitrate$ -c:v libx264 $video_bitrate$ -preset medium $filters$ -y $output_file$",
  "created_at":"2009/10/14 18:36:30 +0000",
  "updated_at":"2009/10/14 19:38:42 +0000"
}

POST /profiles.json

Optional parameters

name:                 Unique Machine-readable name that will identify the profile.
                      Helpful later on for filtering encodings by profile

title:                Human-readable name

extname:              (example: '.mp4') File extension 

width:                (example: 1080) Width in pixels
height:               (example: 720) Height in pixels
upscale:              (default: true) Upscale the video resolution to match your profile
aspect_mode:          (values: letterbox | preserve | constrain | pad | crop,
                       default: letterbox)

two_pass:             (default: false)

video_bitrate:        (example: 3000)
fps:                  (example: 29.97, default: not set) Null value copy the original fps
keyframe_interval     (default: 250) Adds a key frame every 250 frames
keyframe_rate         (example: 0.5) Adds a key frame every 2 seconds

audio_bitrate:        (example: 128)
audio_sample_rate:    (default: 44100)
audio_channels:       (default: not set)

clip_length:          (example: '00:20:00') Sets the clip's duration
clip_offset:          (example: '00:00:10') Clip starts at a specific offset

watermark_url:        Url of the watermark image
watermark_top, 
watermark_bottom,
watermark_left,
watermark_right:      (default: 0) Distance from the top/bottom/right/left of the video frame
                      Works like CSS

watermark_width,
watermark_height:     (default: no resizing) Size of the watermark image

frame_count:          (default: 7) Evenly spaced number of generated screenshots

H264 preset

h264_crf:             (example: 23)
h264_profile:         (example: 'baseline')
h264_level:           (example: '3.1')

JPEG preset

Only one of the following attributes can be set.

frame_count:          (example: 7) Evenly spaced number of thumbnails
frame_offsets:        (example: '2s, 10s, 250f, 400f') Array of offset (Frames or seconds),
frame_interval:       (example: '1000f') Thumbnail interval (Frames or seconds)
Using presets

Required parameters

preset_name

Example request

POST /profiles.json

preset_name: 'h264'

Example response

{
  "id":"40d9f8711d64aaa74f88462e9274f39a",
  "title": "H264 (MP4)",
  "name": "h264",
  "extname":".mp4",
  "width":480,
  "height":320,
  "audio_bitrate": 128,
  "video_bitrate": 500,
  "preset_name": "h264",
  "created_at":"2009/10/14 18:36:30 +0000",
  "updated_at":"2009/10/14 19:38:42 +0000"
}    
Using custom settings

Required parameters

command: line break separated list of encoding commands to run. 
extname: file extension (example: '.mp4')

Example request

POST /profiles.json

title: H264 normal quality
name: h264
extname: .mp4
width: 320
height: 240
audio_bitrate: 128
video_bitrate: 500
aspect_mode: pad
command: ffmpeg -i $input_file$ -c:a libfaac $audio_bitrate$ -c:v libx264 $video_bitrate$ -preset medium $filters$ -y $output_file$

Example response

{
  "id":"40d9f8711d64aaa74f88462e9274f39a",
  "title":"H264 normal quality",
  "extname":".mp4",
  "width":320,
  "height":240,
  "audio_bitrate":128,
  "video_bitrate":500,
  "aspect_mode" "pad",
  "command":"ffmpeg -i $input_file$ -c:a libfaac $audio_bitrate$ -c:v libx264 $video_bitrate$ -preset medium $filters$ -y $output_file$",
  "created_at":"2009/10/14 18:36:30 +0000",
  "updated_at":"2009/10/14 19:38:42 +0000"
}

PUT /profiles/:id.json

Optional parameters

Same as POST /profiles.json but preset_name

Example request

PUT /profiles/40d9f8711d64aaa74f88462e9274f39a.json

title: The best custom profile

Example response

{
  "id":"40d9f8711d64aaa74f88462e9274f39a",
  "title":"The best custom profile",
  "extname":".mp4",
  "width":320,
  "height":240,
  "audio_bitrate":128,
  "video_bitrate":500,
  "command":"ffmpeg -i $input_file$ -c:a libfaac $audio_bitrate$ -c:v libx264 $video_bitrate$ -preset medium $filters$ -y $output_file$",
  "created_at":"2009/10/14 18:36:30 +0000",
  "updated_at":"2009/10/14 19:38:42 +0000"
}

DELETE /profiles/:id.json

Example request

DELETE /profiles/40d9f8711d64aaa74f88462e9274f39a.json

Example response

status: 200

Clouds

GET /clouds/:id.json

Example request

GET /clouds/e122090f4e506ae9ee266c3eb78a8b67.json

Example response

{
  "id": "e122090f4e506ae9ee266c3eb78a8b67",
  "name": "my_first_cloud",
  "s3_videos_bucket": "my-example-bucket",
  "s3_private_access":false,
  "url": "http://my-example-bucket.s3.amazonaws.com/",
  "created_at": "2010/03/18 12:56:04 +0000",
  "updated_at": "2010/03/18 12:59:06 +0000"
}

PUT /clouds/:id.json

Optional parameters

name
s3_videos_bucket
aws_access_key
aws_secret_key

Example request

PUT /clouds/e122090f4e506ae9ee266c3eb78a8b67.json
name: "my_first_cloud"
s3_videos_bucket: "my_own_bucket"
aws_access_key: XQwEwFR
aws_secret_key: XoSV2fV

Example response

{
  "id": "e122090f4e506ae9ee266c3eb78a8b67",
  "name": "my_first_cloud_updated",
  "s3_videos_bucket": "my-example-bucket",
  "s3_private_access": false
  "created_at":"2010/03/18 12:56:04 +0000",
  "url": "http://my-example-bucket.s3.amazonaws.com/",
  "updated_at":"2010/03/18 12:59:06 +0000"
}

Notifications

GET /notifications.json

Example request

GET /notifications.json

Example response

{
  "url": "null", 
  "events": {
    "video_created": false, 
    "video_encoded": false,
    "encoding_progress": false,
    "encoding_completed": false
  }
}

PUT /notifications.json

Example request

PUT /notifications.json
url: "http://example.com/panda_notification"
events['video_encoded'] = true

Example response

{
  url: "http://example.com/panda_notification", 
  events: {
    "video_created": false, 
    "video_encoded": true,
    "encoding_progress": false,
    "encoding_completed": false
  }
}

Error classes and Error messages

Encoding and Video have 2 special attributes when their status is set to ‘fail’

`error_class`: Helps you figure out what happened and what to do next.
`error_message`: Gives you a more descriptive message of the error

The following table list all possible error classes:

  • S3Error

    Whenever the system try to access your bucket and gets an bad response from S3.

  • VideoStatusInvalid

    The original video is in error state and can not be processed. Check the videos error to know the reason (only applied for encodings).

  • CommandInvalid

    The command of your custom profile is not correct. You should take a look at your encoding logfile.

  • EncodingError

    The encoding has failed. You should take a look at your encoding logfile.

  • FileNotFound

    Your original video file doesn’t exist on S3.

  • DownloadFailed

    Panda was not able to download the file from source_url.

  • UnexpectedError

    Error is not expected from the system we will look at it.

  • FormatNotRecognised

    Your file is not a video or audio file valid.

API Errors

When there is an issue with a request, Panda will return the appropriate HTTP status code, along with a JSON object containing the error name and a message. (500 status returns an empty body)

500 (InternalError)

When there is an internal error a 500 status will be returned along with additional information in the message. Whenever a 500 error occurs our technical team is notified of the issue and will investigate the root of the problem immediately. If your experience a recurring issue, please submit a support ticket to support.pandastream.com

401 NotAuthorized

When the signature parameter of a request is not correct, the server returns a status code 401 and a descriptive message. If you receive this error please ensure that you are constructing and encoding the signature parameter as described in the API Authentication section below.

Example response

{"error" : "NotAuthorized", "message" : "Signatures do not match"}

404 RecordNotFound

A record with the id supplied could not be found.

Example response (example)

{"error" : "RecordNotFound", "message" : "Couldn't find Video with ID=X"}

400 BadRequest

This error will be returned in two cases. Either because you have requested a response format that is not supported (currently only JSON is supported, so all urls must end in .json), or you have not submitted all of the required parameters to a method.

Example response (2 examples)

{"error" : "BadRequest", "message" : "Currently only .json is supported as a format"}

{"error" : "BadRequest", "message" : "All required parameters were not supplied: access_key, signature, timestamp"}

413 FileSizeLimitExceeded

If you attempt to upload a file > 10MB with a sandbox account, the following error will be returned.

Example response

{"error" : "BadRequest", "message" : "File size limit for this account is set to 10485760 bytes"}

Client Notifications [deprecated!]

Client can be notified when all encodings of a video are finished. This can be achieved by providing state_update_url parameter when posting a video. Panda will make GET HTTP request to this URL. If URL contains $id string, it will be replaced with the id of video.

i.e http://mypandasite.com/videos/update?id=$id

API Authentication

For examples of authentication implementation please refer to the Ruby gem and example PHP client.

The Panda API requires all requests must also be signed to ensure they are valid and authenticated. For GET and DELETE requests the additional parameters must be url encoded and added to the parameters in the url. When making a POST or PUT request they should be included in the usual parameters payload submitted.

The access_key and secret_key used to authenticate the request are provided when you sign up for your Panda account. Your keys can always be found by logging in to your account by visiting pandastream.com

A correctly signed request contains the following additional parameters:

access_key: Provided when you sign up for Panda
cloud_id:   Provided when you sign up for Panda
timestamp:  Current UTC time in iso8601 format
signature:  HMAC signature generated as described below

Build the signature

The signature is generated using the following method:

1) Create a canonical_querystring by url encoding all of the parameters and the values, and joining them into one stringusing the = character to separate keys and their values, and the & character to separate the key value pairs. All parameters and values have to be joined in the alphabetical order.

A typical canonical_querystring might look as follows: access_key=85f8dbe6-b998-11de-82e1-001ec2b5c0e1&cloud_id=bd54547d152e91104d82c0a81e7d5ff2&timestamp=2009-10-15T15%3A38%3A42%2B01%3A00 … other parameters such as those in the POST request would also be added to this string.

2) Construct the string_to_sign by concatenating the uppercase HTTP request method (GET, POST, PUT or DELETE), hostname (api.pandastream.com or api-eu.pandastream.com), request uri (e.g. /videos.json) and canonical_querystring with newlines (\n).

3) To generate the signature, first HMAC SHA256 encode the complete string_to_sign using your secret_key as the key. For example, using Ruby: hmac = HMAC::SHA256.new(secret_key) then hmac.update(string_to_sign)

4) Then take the binary digest of the hmac output and base 64 encode it. Make sure to remove any newline characters at the end. In Ruby you would do Base64.encode64(hmac.digest).chomp

5) Finally, URL-escape the last result. This is now your signature that will be sent to Panda. In ruby you would do CGI.escape(signature)

6) The signature could be rejected by the server for the following reasons: the request is a POST request and the signature has already been used the request is POST /videos.json and the timestamp is expired for 30 minutes the request is not POST and the timestamp is expired for 5 minutes

Worked example

Assume that we wish to get all videos we have uploaded to our cloud (US account)

cloud_id = '123456789'
access_key = 'abcdefgh'
secret_key = 'ijklmnop'

We first construct the request uri

api.pandastream.com/v2/videos.json

We send the following query parameters

access_key  abcdefgh
cloud_id    123456789
timestamp   2011-03-01T15:39:10.260762Z

The signature is generated by signing the following string

GET\napi.pandastream.com\n/videos.json\naccess_key=abcdefgh&cloud_id=123456789&timestamp=2011-03-01T15%3A39%3A10.260762Z

This should be signed by generating the HMAC SHA256 hex digest with secret key ijklmnop

kVnZs%2FNX13ldKPdhFYoVnoclr8075DwiZF0TGgIbMsc%3D

The api request then becomes

GET /videos.json
QUERY
access_key=abcdefgh&cloud_id=123456789&timestamp=2011-03-01T15%3A39%3A10.260762Z&signature=kVnZs%2FNX13ldKPdhFYoVnoclr8075DwiZF0TGgIbMsc%3D

Here is the request and response made with curl

$ curl http://api.pandastream.com/v2/videos.json?access_key=abcdefgh&cloud_id=123456789&timestamp=2011-03-01T15:39:10.260762Z&signature=kVnZs%2FNX13ldKPdhFYoVnoclr8075DwiZF0TGgIbMsc%3D
status: "200"
body: "[]"

Things to look into if you’re having signature issues

  • Ensure that the timestamp is uppercase and strict iso8601 format.
  • Check the url in the string_to_sign does not include /v2, even though /v2 is required as part of the actual url you will request when accessing the api.
  • Only url encode the parameter values of the canonical_querystring and not the whole string_to_sign.
  • The signature should end in an = sign and not contain any more characters afterwards. For example, if your HMAC library is adding the characters 3D after the = you must remove these.
  • Make sure you use the binary digest of the HMAC and not any other outputs such as the hex digest.
  • Make sure that URL encoded characters are uppercase (%3A and not %3a).
  • Make sure inside the string_to_sign you use GET for GET request, POST for POST request, etc…