Table of Contents
1. Introduction
Struggling with direct URL links to bunny.net videos? Cannot get back content you uploaded?
This is are common people problems but only few knows how to solve them. In this knowledge article I want to explain you different ways to get direct URL to your video content as well as I will show you how to download whole catalog.
2. Download content without API
If you don’t want to play with Postman, scripting and API calls then you can read this and get benefit of UI.
Go to Stream section, select your video library then select your video and use triple dots menu to download it as zip.
Zip archive has a name data.zip and present structure like below example, where you have mp4 files and catalogs with different resolutions of ts MPEG format. Note down presence of original file which is dependent from Encoding settings ‘Keep Original Files’ of your Stream section
drwxr-xr-x@ 240p
drwxr-xr-x@ 360p
drwxr-xr-x@ 480p
drwxr-xr-x@ 720p
-rw-r--r--@ master.m3u8
-rw-r--r--@ original
-rw-r--r--@ play_240p.mp4
-rw-r--r--@ play_360p.mp4
-rw-r--r--@ play_480p.mp4
-rw-r--r--@ play_720p.mp4
-rw-r--r--@ playlist.m3u8
-rw-r--r--@ preview.gif
-rw-r--r--@ preview.webp
-rw-r--r--@ response.mp4
drwxr-xr-x@ seek
-rw-r--r--@ thumbnail.jpg
-rw-r--r--@ thumbnail_1.jpg
-rw-r--r--@ thumbnail_2.jpg
-rw-r--r--@ thumbnail_3.jpg
-rw-r--r--@ thumbnail_4.jpg
-rw-r--r--@ thumbnail_5.jpg
This way you can download your data without any coding or API calls.
3. Understanding API
There are 3 API endpoints with bunny:
bunny.net API – this is main API that let you manage most of the stuff and you will get API key for that in your account settings
Edge Storage API – when you need to download file from the storage or upload there, here you are.
Stream API – this is for managing existing video library which you can create with main API
In this article I will focus on Edge and main API because those will let you access video content.
3.1. Bunny.net API Postman authorization setup
As I mentioned you will get API key from your account settings under section API. Then you can setup it in Postman like below or save it to use in curl commands.
3.2. Creating Example Video Library
To create example video library run:
curl --location 'https://api.bunny.net/videolibrary' \
--header 'Content-Type: application/json' \
--header 'AccessKey: ********' \
--data '{
"name":"exampleVideoLibrary"
}'
you should get response similar to below
{
"Id": 228555,
"Name": "exampleVideoLibrary",
"VideoCount": 0,
"TrafficUsage": 0,
"StorageUsage": 0,
"DateCreated": "2024-04-07T19:24:53.8785176Z",
"ReplicationRegions": [
""
],
"ApiKey": "84e5d0d0-ba6a-4cda-9b816283abef-43b1-4a17",
"ReadOnlyApiKey": "70da7626-d289-414b-9100d79e6f4b-ee4d-47bc",
"HasWatermark": false,
"WatermarkPositionLeft": 0,
"WatermarkPositionTop": 0,
"WatermarkWidth": 0,
"PullZoneId": 2130446,
"StorageZoneId": 634717,
"WatermarkHeight": 0,
"EnabledResolutions": "240p,360p,480p,720p,1080p",
"ViAiPublisherId": null,
"VastTagUrl": null,
"WebhookUrl": null,
"CaptionsFontSize": 20,
"CaptionsFontColor": "#fff",
"CaptionsBackground": "#000",
"UILanguage": "en",
"AllowEarlyPlay": false,
"PlayerTokenAuthenticationEnabled": false,
"AllowedReferrers": [],
"BlockedReferrers": [],
"BlockNoneReferrer": true,
"EnableMP4Fallback": true,
"KeepOriginalFiles": true,
"AllowDirectPlay": true,
"EnableDRM": false,
"DrmVersion": 0,
"AppleFairPlayDrm": {
"Enabled": false,
"CertificateId": null,
"CertificateExpirationDate": null,
"Provider": null
},
"GoogleWidevineDrm": {
"Enabled": false,
"CertificateId": null,
"CertificateExpirationDate": null,
"Provider": null,
"SdOnlyForL3": false,
"MinClientSecurityLevel": null
},
"Bitrate240p": 600,
"Bitrate360p": 800,
"Bitrate480p": 1400,
"Bitrate720p": 2800,
"Bitrate1080p": 5000,
"Bitrate1440p": 8000,
"Bitrate2160p": 25000,
"ApiAccessKey": null,
"ShowHeatmap": false,
"EnableContentTagging": true,
"PullZoneType": 1,
"CustomHTML": null,
"Controls": "play-large,play,progress,current-time,mute,volume,captions,settings,airplay,pip,fullscreen",
"PlayerKeyColor": "#ff7755",
"FontFamily": "Rubik",
"WatermarkVersion": 0,
"EnableTranscribing": false,
"EnableTranscribingTitleGeneration": false,
"EnableTranscribingDescriptionGeneration": false,
"TranscribingCaptionLanguages": [],
"RememberPlayerPosition": false
}
Returned StorageZoneId will let you localize where uploaded videos end up and ApiKey is to access Stream API. You can reset API key if you need but I haven’t found call to reset ReadOnlyApiKey.
curl --request POST \
--url https://api.bunny.net/videolibrary/634717/resetApiKey \
--header 'AccessKey: *******'
3.3. Accessing linked to Library Storage
Because you have storage Zone ID you can ask for storage Zone individual password
curl --location 'https://api.bunny.net/storagezone/634717' \
--header 'AccessKey: ******'
In return you will have details
{
"Id": 634717,
"UserId": "1210c3d5-6a6e-4136-a135-91a4248be30b",
"Name": "vz-084ff1d7-d63",
"Password": "0ef54d0d-bb8b-4c01-96005e858992-6752-47b0",
"DateModified": "2024-04-07T19:24:53",
"Deleted": false,
"StorageUsed": 0,
"FilesStored": 0,
"Region": "DE",
"ReplicationRegions": [],
"PullZones": [
{
"Id": 2130446,
"Name": "vz-084ff1d7-d63",
"OriginUrl": "",
"Enabled": true,
"Suspended": false,
"Hostnames": [
{
"Id": 3551293,
"Value": "vz-084ff1d7-d63.b-cdn.net",
"ForceSSL": false,
"IsSystemHostname": true,
"HasCertificate": true,
"Certificate": null,
"CertificateKey": null
}
],
"StorageZoneId": 634717,
...
}
Note down
“Name”: “vz-084ff1d7-d63”,
“Password”: “0ef54d0d-bb8b-4c01-96005e858992-6752-47b0”
because this you will need to list content of storage and to download particular file.
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0'
response:
[]
Now video library and linked storage are empty so it give you empty array. Time to upload some videos.
3.4. Uploading example videos
3.4.1. Uploading directly to hidden Storage
For that you can use storage API to upload directly to hidden storage although it is not right approach because that storage was created for Video Library purpose and it’s not visible in bunny web page dashboard, neither video you will upload will be visible in video library list. Storage is to back up Video Library content.
curl --location --request PUT 'https://storage.bunnycdn.com/vz-084ff1d7-d63/play_240p.mp4' \
--header 'Content-Type: video/mp4' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0' \
--data '@/videos/exampleVideo.mp4'
response
{
"HttpCode": 201,
"Message": "File uploaded."
}
If you check storage again
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0'
you will see the file
[
{
"Guid": "b8cfe836-5a5b-4c50-bec4-591591af8244",
"StorageZoneName": "vz-084ff1d7-d63",
"Path": "/vz-084ff1d7-d63/",
"ObjectName": "play_240p.mp4",
"Length": 2598574,
"LastChanged": "2024-04-07T20:00:22.601",
"ServerId": 613,
"ArrayNumber": 0,
"IsDirectory": false,
"UserId": "1210c3d5-6a6e-4136-a135-91a4248be30b",
"ContentType": "",
"DateCreated": "2024-04-07T20:00:22.601",
"StorageZoneId": 634717,
"Checksum": "977FD2439E73CB30EA452C833DC3293B46A0B6AD8AC565360E8EB6E02EC26FFC",
"ReplicatedZones": ""
}
]
but you will not see it in the webpage UI that’s because metadata for video was not created
3.4.2. Uploading via Stream API
When you created video library you got
“ApiKey”: “84e5d0d0-ba6a-4cda-9b816283abef-43b1-4a17”
in return and that you have to use to access Stream API. First step is to create video metadata and secondly upload binary file.
curl --location 'https://video.bunnycdn.com/library/228555/videos' \
--header 'Content-Type: application/json' \
--header 'AccessKey: 84e5d0d0-ba6a-4cda-9b816283abef-43b1-4a17' \
--data '{
"title": "SomeTitle"
}'
Note down guid (video ID) from response as you need it to upload binary file referencing that id.
{
"videoLibraryId": 228555,
"guid": "5e778351-78d5-493d-b764-86b78ad566ff",
"title": "SomeTitle",
"dateUploaded": "2024-04-07T20:13:19.3811667Z",
"views": 0,
...
"transcodingMessages": []
}
Run below command to upload example video to created Library and video metadata
curl --location --request PUT 'https://video.bunnycdn.com/library/228555/videos/5e778351-78d5-493d-b764-86b78ad566ff' \
--header 'Content-Type: video/mp4' \
--header 'AccessKey: 84e5d0d0-ba6a-4cda-9b816283abef-43b1-4a17' \
--data '@/videos/descript.mp4'
response telling you it was successfull
{
"success": true,
"message": "OK",
"statusCode": 200
}
Checking storage again will tell you that new catalog was created for video
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0'
[
{
"Guid": "245c357e-7f08-42d6-ac90-9ea66b7c4c61",
"StorageZoneName": "vz-084ff1d7-d63",
"Path": "/vz-084ff1d7-d63/",
"ObjectName": "5e778351-78d5-493d-b764-86b78ad566ff",
"Length": 0,
"LastChanged": "2024-04-07T21:12:58.398",
"ServerId": 0,
"ArrayNumber": 0,
"IsDirectory": true,
"UserId": "1210c3d5-6a6e-4136-a135-91a4248be30b",
"ContentType": "",
"DateCreated": "2024-04-07T21:12:58.398",
"StorageZoneId": 634717,
"Checksum": null,
"ReplicatedZones": null
},
{
"Guid": "b8cfe836-5a5b-4c50-bec4-591591af8244",
"StorageZoneName": "vz-084ff1d7-d63",
"Path": "/vz-084ff1d7-d63/",
"ObjectName": "play_240p.mp4",
"Length": 2598574,
"LastChanged": "2024-04-07T20:00:22.601",
"ServerId": 613,
"ArrayNumber": 0,
"IsDirectory": false,
"UserId": "1210c3d5-6a6e-4136-a135-91a4248be30b",
"ContentType": "",
"DateCreated": "2024-04-07T20:00:22.601",
"StorageZoneId": 634717,
"Checksum": "977FD2439E73CB30EA452C833DC3293B46A0B6AD8AC565360E8EB6E02EC26FFC",
"ReplicatedZones": ""
}
]
If you visit right now bunny net web page you will noticed new video in video library
3.5. Downloading particular videos
If you decide to download it via button ‘Download’ then you will get data.zip which is compressed content of catalog that you can get with following API call
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/5e778351-78d5-493d-b764-86b78ad566ff/' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0'
So if you want to download file instead listing catalog content you need to point to file, the object that has IsDirectory property set as false. For example
{
"Guid": "5485ec58-1780-4dca-a80a-d144b71fe699",
"StorageZoneName": "vz-084ff1d7-d63",
"Path": "/vz-084ff1d7-d63/5e778351-78d5-493d-b764-86b78ad566ff/",
"ObjectName": "play_360p.mp4",
"Length": 1729453,
"LastChanged": "2024-04-07T21:13:16.141",
"ServerId": 632,
"ArrayNumber": 0,
"IsDirectory": false,
"UserId": "1210c3d5-6a6e-4136-a135-91a4248be30b",
"ContentType": "",
"DateCreated": "2024-04-07T21:13:16.141",
"StorageZoneId": 634717,
"Checksum": "C323EE4CACB1D0E72A1EEAED1A7DFA694FC8B090C0106DDAA3906188F7FDD11E",
"ReplicatedZones": ""
}
Then to download run command
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/5e778351-78d5-493d-b764-86b78ad566ff/play_360p.mp4' \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0' \
-o play_360p.mp4
# or including key in parameter
curl --location 'https://storage.bunnycdn.com/vz-084ff1d7-d63/5e778351-78d5-493d-b764-86b78ad566ff/play_360p.mp4?accessKey=0ef54d0d-bb8b-4c01-96005e858992-6752-47b0&download' \
-o play_360p.mp4
3.6. Downloading catalog with all video files
For that basically run curl that pointing to catalog with parameter download.
curl --location https://storage.bunnycdn.com/vz-084ff1d7-d63/5e778351-78d5-493d-b764-86b78ad566ff/?download \
--header 'AccessKey: 0ef54d0d-bb8b-4c01-96005e858992-6752-47b0' \
-o 5e778351-78d5-493d-b764-86b78ad566ff.zip
3.7. Getting link to share via player
Link that will work with player on the webpage can be taken via Pull Zone.
If you are operating on bunny.net UI you cannot create pull zone for storage linked with Video Library for the Stream but because we are in API section such things are absolutely possible.
Therefore you can run
curl --location 'https://api.bunny.net/pullzone' \
--header 'Content-Type: application/json' \
--header 'AccessKey: *********' \
--data '{
"Name": "PullZoneForHiddenStorage",
"OriginType": 2,
"StorageZoneId": 634717
}'
Origin type 2 is for storage zone and storage zone id pointing to same storage that backing your video library files.
Response:
{
"Id": 2132300,
"Name": "PullZoneForHiddenStorage",
"OriginUrl": "",
"Enabled": true,
"Suspended": false,
"Hostnames": [
{
"Id": 3554220,
"Value": "PullZoneForHiddenStorage.b-cdn.net",
"ForceSSL": false,
"IsSystemHostname": true,
"HasCertificate": true,
"Certificate": null,
"CertificateKey": null
}
],
"StorageZoneId": 634717,
"EdgeScriptId": -1,
"EdgeScriptExecutionPhase": 0,
"MagicContainersAppId": null,
"AllowedReferrers": [],
"BlockedReferrers": [],
"BlockedIps": [],
...
"ZoneSecurityEnabled": false,
"ZoneSecurityKey": "9d7e4457-7b94-4e94-b11b-8774b8b4de95",
"CnameDomain": "b-cdn.net",
...
"VideoLibraryId": -1,
...
"OriginType": 2,
"OriginLinkValue": "vz-084ff1d7-d63",
}
Now you can get access to the file with pull zone new hostname
curl --location 'https://pullzoneforhiddenstorage.b-cdn.net/play_240p.mp4'
or files created by transcoding uploaded content to Video Library
curl --location 'https://pullzoneforhiddenstorage.b-cdn.net/5e778351-78d5-493d-b764-86b78ad566ff/play_360p.mp4'
URLs exposed via pull zone can be used as source for Presto Player to include in your web page.
This does not work with URLs to storage.
ZoneSecurityKey is ‘Url Token Authentication Key’ that you can see in UI.
3.7.1. Enable token authentication
I couldn’t find API call that will enable token authentication. Updating Pull Zone property ZoneSecurityEnabled as true does not work. But I found workaround.
First you create Edge Rule that will enable that, then you can remove that edge rule to keep things clean. Tested & works although looks silly.
curl --location 'https://api.bunny.net/pullzone/2132312/edgerules/addOrUpdate' \
--header 'Content-Type: application/json' \
--header 'AccessKey: ******' \
--data '{
"ActionType": 9,
"TriggerMatchingType": 0,
"Enabled": true,
"Triggers": [
{
"Type": 9,
"PatternMatchingType": 1,
"PatternMatches": [
"GET"
]
}
]
}'
As response you getting guid that you will use to delete that rule
{
"Guid": "6596594f-ee3c-4a8a-b7ad-b9ebcdcd074d",
"ActionType": 9,
"ActionParameter1": "",
"ActionParameter2": "",
"Triggers": [
{
"Type": 9,
"PatternMatches": [
"GET"
],
"PatternMatchingType": 1,
"Parameter1": null
}
],
"ExtraActions": [],
"TriggerMatchingType": 0,
"Description": null,
"Enabled": true
}
Now deleting:
curl --location --request DELETE 'https://api.bunny.net/pullzone/2132312/edgerules/6596594f-ee3c-4a8a-b7ad-b9ebcdcd074d' \
--header 'AccessKey: *****'
3.7.2. Sign request URL
Once authentication is enabled you cannot access same video without token. To generate token you can use simple shell commands
security_key="8e543f2a-a21d-4df9-96b6-3e97debf8860"
expiration_time=3600
expires=$(($(date +%s) + expiration_time))
signature_path="/play_240p.mp4"
hashable_base=$security_key$signature_path$expires
token=$(echo -n $hashable_base | shasum -a 256 | xxd -r -p | base64)
token=$(echo $token | tr -d '\n' | tr '+' '-' | tr '/' '_' | tr -d '=')
echo "https://pullzoneforhiddenstorage.b-cdn.net$signature_path?token=$token&expires=$expires"
This URL you can put as well as source for Presto Player and display on your website.
4. Conclusion
Important point to mention is all examples are rather educational than real implementation. All API request should be handled as backend code on webserver side. This include signing URLs of course. Therefore I am happy you will get this as initial starting point and enjoy that knowledge in your projects.
Have a nice coding!