Manage Bunny Stream from the terminal with the bunny.net CLI

Posted by:

Today we're adding bunny stream to the bunny.net CLI so you or your AI agent can manage Bunny Stream from the same terminal you use for Bunny Database, Storage, DNS, and Edge Scripts. Create a library, upload or fetch videos, style the player, lock down playback, add captions, and generate titles and chapters from a transcript, all with one tool.

Each command does one job and runs the same way in your shell, a CI pipeline, or an AI agent’s session. String a few together and a video workflow becomes a script you can run again whenever you need it.

Three commands take a local file to a player URL:

bunny stream libraries create my-library --link
bunny stream videos upload ./launch-demo.mp4 --title "Launch demo"
bunny stream videos embed

--link connects the current directory to the new library through .bunny/stream.json, so every later command in that project knows where to look. Leave the flag off and libraries create asks, along with two more offers: saving the library's credentials to .env so our Stream examples work out of the box, and adding your first videos straight away. Then it prints what to do next.

The upload reports a status of Uploaded or Processing, because bunny.net keeps encoding after the command returns. videos embed with no GUID opens a picker of the library's videos and prints the player embed URL for the one you choose.

In a directory with no link, pass the library ID with --lib, or run bunny stream link to connect one. If you run a command interactively with neither, the CLI opens a library picker and then offers to link the directory to the library you choose.

Libraries, regions and API keys

libraries create stores a new library in DE, and you can add replication regions when you create it or later. bunny stream regions lists the codes:

bunny stream regions

Code   Name           Role          Suggested
 DE     Frankfurt      main          no
 UK     London         replication   no
 SE     Stockholm      replication   no
 NY     New York       replication   yes
 LA     Los Angeles    replication   yes
 SG     Singapore      replication   yes
 SYD    Sydney         replication   no
 BR     Sao Paulo      replication   no
 JH     Johannesburg   replication   no

bunny stream libraries create my-library --replication-regions NY,SG

You can add regions later with libraries update --add-replication-regions, but you can’t remove one once it’s added, so choose with care.

libraries update sends only the flags you pass and leaves every other setting alone. Run it with no flags and it opens a small editor for the name, enabled resolutions, and transcribing, prefilled with the current values:

bunny stream libraries update my-library --resolutions 720p,1080p
bunny stream libraries update my-library --keep-original --mp4-fallback

The Stream API key stays out of sight. libraries show never prints it, in any output format, and libraries credentials masks it until you add --show-secret. For an app that needs the key, libraries credentials --save-env writes it to .env alongside the library ID. --rotate resets the key after a confirmation, so check what else is using it first.

Two ways to add a video

videos upload sends a local file. Files over 2 GB switch to resumable TUS uploads automatically: the CLI sends 64 MiB chunks, and a failed chunk retries from the offset the server reports it holds, so a blip halfway through a large file carries on from there. Resume state lives in the running command, so a cancelled upload starts again from zero on the next run.

Each upload creates the video entry first, then sends the bytes. If the bytes fail, the CLI checks what bunny.net received. An entry with no data is deleted so a retry leaves no orphans. An entry that may have received data stays, and the CLI prints its GUID in a warning.

videos fetch hands bunny.net a URL and lets our servers do the download, so the file never passes through your machine. The command returns once the fetch is queued, and the video shows up in videos list after it's fetched and encoded. Add --header for an origin that needs authentication:

bunny stream videos fetch https://example.com/video.mp4 --header "Authorization: Bearer abc"

There's no videos create, since every video starts as a file or a URL. Both commands take --collection to drop the video straight into a collection, and --thumbnail-time to pick the main thumbnail frame during ingest.

Style the player and sign the embeds

Player settings live on the library, covering colors, fonts, captions, controls, playback speeds, and the heatmap:

bunny stream libraries player update my-library --color "#FF7755" --speeds 0.5,1.0,1.15,2.0 --heatmap
bunny stream libraries player reset my-library

player reset returns everything to the dashboard defaults after a confirmation.

security update handles allowed and blocked referrers, direct access, embed and CDN tokens, and MediaCage Basic DRM:

bunny stream libraries security update my-library --embed-token --block-direct-access --allow-referrer example.com

Settings that are already in place get skipped, and if a change fails partway, the CLI tells you what went through. You can run the same command again to finish the job.

With embed tokens on, videos embed signs the URL with the token key from the library's linked pull zone:

bunny stream videos embed 1a2b3c4d-... --expires 1h

security show --rotate resets that key after a confirmation, and every signed URL you've already handed out stops working.

Collections, captions and housekeeping

Collections group a library's videos. collections delete removes every video inside the collection along with it, and the confirmation names how many are about to go, so move anything you want to keep out first:

bunny stream videos update 1a2b3c4d-... --collection ""
bunny stream collections delete 8a7b6c5d-...

videos captions add uploads a .vtt or .srt file you wrote yourself and prints what the API's validator says, including warnings for an accepted file with non-breaking issues. Leave out the video or the language and it asks for them:

bunny stream videos captions add ./captions.vtt 1a2b3c4d-... en --label "English"

For videos that have been around a while, videos resolutions lists what's configured, encoded, and stored, and videos cleanup deletes renditions, MP4 fallbacks, or the stored original. Start with --dry-run to see what would be deleted without actually deleting anything:

bunny stream videos cleanup 1a2b3c4d-... --non-configured --dry-run

videos stats reports views and watch time, with --heatmap and --play-data for the detail.

Three video commands can cost money, and they warn you before they run: videos encode, videos transcribe, and videos smart.

videos encode re-encodes a video with the library's current settings. This requires the original file, so --keep-original must have been enabled when the video was uploaded. Standard encoding is included at no extra cost, so on most libraries this is free. With Premium Encoding, every output it regenerates is billed per minute of output and per output codec. You switch a library to Premium Encoding with libraries update --encoding-tier premium.

videos transcribe costs $0.10 per language-minute of audio and prints an estimate before it starts. Pass --source-language for an exact figure. Without it you get a range, since the auto-detected spoken language may add one more language to the bill:

bunny stream videos transcribe 1a2b3c4d-... --target-languages en,de --source-language en

--overwrite replaces a transcription the video already has and overrides the library's transcribing defaults.

Library settings can cost money too. If the library already transcribes automatically, uploads trigger a transcription on their own. A re-encode reuses the existing transcription, so you only pay to transcribe again if the transcription languages have changed, and videos encode tells you what that would cost.

videos smart reads a video's transcript and generates a title, description, chapters, or moments:

bunny stream videos smart 1a2b3c4d-... --title --chapters

videos smart never transcribes a video without asking. It shows the estimated cost first, and an unattended run stops with the figure unless you approve it up front with --transcribe.

Built for scripts and AI agents

Every command works unattended. With --output json or --force, or when there’s no TTY, the CLI never prompts. A command that can't determine which library to use exits with a hint to pass one or link the directory, so a CI job fails fast and never hangs on a question nobody is there to answer.

Deletes, cleanups and unlinks need --force to run non-interactively. On destructive commands and videos smart, --force also turns the pickers off, so the CLI can't act on a library or video you didn't name. That's the guarantee you want when an agent is at the keyboard.

Get started

Install the CLI, log in, and create a library:

npm install -g @bunny.net/cli
bunny login
bunny stream libraries create my-library

libraries create walks you through linking the directory, saving the credentials to .env, and adding your first videos. To use a library you already have, run bunny stream link and pick it from the list. Then upload something and check it arrived:

bunny stream videos upload ./video.mp4
bunny stream videos list

When we shipped bunny sites, we said Stream support was coming next. It's in the CLI now, alongside the rest of the bunny.net stack:

The CLI is open source and built in public. If something breaks or a Stream task you rely on is missing, tell us on GitHub or Discord.

Comments require cookies. to view and post.