Platform documentation & tutorials
Integrate Panda with your web app
- Full Guides
- Integration
- Profiles
- Downloads
- More...
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×tamp=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×tamp=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×tamp=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×tamp=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_signdoes not include/v2, even though/v2is required as part of the actual url you will request when accessing the api. - Only url encode the parameter values of the
canonical_querystringand not the wholestring_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 characters3Dafter 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
GETfor GET request,POSTfor POST request, etc…