> ## Documentation Index > Fetch the complete documentation index at: https://bunny.net/docs/llms.txt > Use this file to discover all available pages before exploring further. # Migrate from Vimeo to Bunny Stream > A step-by-step walkthrough for migrating a Vimeo video library into Bunny Stream using the Bunny CLI. > [!TIP] > > Run `bunny skills install --global` so AI coding agents, such as Claude Code, Cursor, and Codex, can run this migration for you. Then ask your agent: "Migrate my Vimeo videos to Bunny Stream." > [!WARNING] > > `bunny stream import` is in [Public Preview](https://bunny.net/docs/product-release-stages.md#stage-2-public-preview) and still in development. Commands, flags, and output may change, so what you see can differ from the examples in this guide. To report a bug, [open an issue on the Bunny CLI repository](https://github.com/BunnyWay/cli/issues/new). [Bunny Stream](https://bunny.net/stream/) bills only for the storage and bandwidth you use. See the [Bunny Stream pricing page](https://bunny.net/pricing/stream/) for current rates. ## Overview ![Diagram of the Bunny CLI listing Vimeo folders and videos and Bunny Stream fetching each file directly from Vimeo](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/how-bunny-stream-import-works.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=88da7f68b8e0f83b47f5a0e0c3419c61) The Bunny CLI lists every video on your Vimeo account and hands each one to Bunny Stream, which fetches the best available file directly from Vimeo. Nothing is downloaded to your machine. Each Vimeo folder becomes a Bunny Stream collection with the same name. Each imported video is tagged with its Vimeo video ID under the `vimeoId` metatag, so running the import again skips videos that are already imported. ## Scope The import copies the video files, their titles, descriptions, and tags, and your folder structure. Settings that control who can watch your videos and where stay on Vimeo, so plan to set up their Bunny equivalents. | Vimeo item | Migrated | Notes | | - | - | - | | Video file | Yes | The original upload when Vimeo has it, otherwise the highest resolution download or MP4 file. | | Title | Yes | | | Description | Yes | Stored in the `description` metatag on the Bunny video. | | Tags | Yes | Stored in the `keywords` metatag on the Bunny video. | | Folders | Yes | Each folder becomes a collection with the same name. An existing collection whose name differs only in case is reused. | | Video ID | Yes | Stored in the `vimeoId` metatag on the Bunny video. | | Videos above 2160p or longer than 72 hours | No | Bunny Stream rejects them. The dry run warns about them before you import. | | Privacy settings (private, unlisted, password) | No | See the privacy note below. | | Embed settings and domain restrictions | No | Use Bunny's library security and [player settings](https://bunny.net/docs/stream/player-settings.md) instead. | | Captions and subtitles | No | Upload them with `bunny stream videos captions add`, or generate new ones with [transcription](https://bunny.net/docs/stream/transcribing.md). | | Custom thumbnails | No | Bunny generates new thumbnails when it encodes each video. | | Chapters | No | | | Showcases | No | Folders are imported as collections, but showcases are not. | | Comments, likes, and analytics | No | Bunny Stream collects its own [statistics](https://bunny.net/docs/stream/statistics.md) from the first view. | > [!WARNING] > > **Private and unlisted videos are not private on Bunny.** The import copies every video your token can read, including private, unlisted, and password-protected ones, but not their privacy settings. Every imported video follows the security settings of its Bunny library, and a new library allows playback from any site. On Bunny, a video that was private or unlisted on Vimeo plays for anyone with its URL until you protect the library. Before you publish the new URLs, set up [Allowed domains](https://bunny.net/docs/stream/security-options.md#domains) to replace Vimeo's embed domain restrictions, and turn on [embed view token authentication](https://bunny.net/docs/stream/token-authentication.md) or [CDN token authentication](https://bunny.net/docs/stream/security.md#cdn-token-authentication) for videos that only signed-in viewers should see. If the videos that need protection are in their own Vimeo folders, you can import those folders into a separate library with `--folder` and give that library stricter settings. ## Prerequisites * A [bunny.net account](https://dash.bunny.net/auth/register) * A [Vimeo](https://vimeo.com) account on a plan that allows video file access: Standard, Advanced, or Enterprise, or a legacy Plus, Pro, Business, or Premium plan. Free and Starter plans cannot download video files through the API. ## Migrate your videos **1. Install and authenticate the Bunny CLI** Install the Bunny CLI with the shell installer or npm: > [tab] curl > > ```sh theme={null} > curl -fsSL https://cli.bunny.net/install.sh | sh > ``` > > The installer downloads a prebuilt binary for your platform and places it on your `PATH`. > [tab] npm > > ```sh theme={null} > npm install -g @bunny.net/cli > ``` See [Installation](https://bunny.net/docs/cli/installation.md) for more options. Then authenticate the CLI with your bunny.net account: ```sh theme={null} bunny login ``` `bunny login` opens your browser to complete authentication and stores the credentials locally. The first time you log in from an interactive terminal, it also offers to install the agent skill for AI coding tools. On a remote machine or in CI without a browser, run `bunny login --api-key "$BUNNYNET_API_KEY"` instead, with a key from [Account Settings > API key](https://dash.bunny.net/account/api-key). **2. Create a Bunny Stream library** Create a library for the imported videos, or skip this step to use an existing one: ```sh theme={null} bunny stream libraries create migrate-from-vimeo --replication-regions NY,SG ``` ```sh theme={null} ✓ Created video library migrate-from-vimeo (ID: 12345), replicated to DE, NY, SG. ``` DE is always the main storage region. Replicate to the regions closest to your audience, from `UK`, `SE`, `NY`, `LA`, `SG`, `SYD`, `BR`, and `JH`. You can't remove a [replication region](https://bunny.net/docs/stream/replication.md) later. > [!NOTE] > > Set encoding options, such as `--resolutions` or `--encoding-tier`, when you create the library. Changes you make later apply only to new uploads, not to videos that are already imported. Run `bunny stream libraries create --help` to see every option, or read [Encoding](https://bunny.net/docs/stream/encoding.md). When the CLI asks how to add videos, choose **Later**. You start the import in the next steps. Copy the library ID from the output. You pass it to `--library` in the import commands. The flag also accepts the library's name, and `--lib` works as a short alias. **3. Create a Vimeo access token** The Bunny CLI needs a Vimeo personal access token with these scopes: * **Public** and **Private**: to read every video on your account, including private and unlisted ones * **Video Files**: to read the download links for each video Open [My Apps](https://developer.vimeo.com/apps) on the Vimeo developer site. If you don't have an app yet, click **Create an app**. Enter an app name, such as migrate-to-bunny, and a brief description, select **No** for whether people besides you will access the app, accept the Vimeo terms, and click **Create App**. ![The Create a new app form on the Vimeo developer site](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/vimeo-create-app.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=ab75b0cbdd80ea21a7cb0324c3f66dd8) On the app's page, select **Generate Access Token** under **Authentication** in the left-hand navigation: ![The Vimeo app page with Generate Access Token selected in the navigation](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/vimeo-generate-access-token.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=9747ff09d6d880a12319f07e18fc30b8) Choose **Authenticated (you)**, so the token can read the private videos on your account. Under **Scopes**, **Public** is already checked, so also check **Private** and **Video Files**. Click **Generate**. ![The Generate Access Token form in Vimeo with the Public, Private, and Video Files scopes checked](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/vimeo-token-scopes.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=cf3cfc8d864c42bbc247852ac838465f) The new token appears under **Personal Access Tokens** with the scopes private, video\_files, and public. Copy the value from the **Token** column. ![The Personal Access Tokens list in Vimeo showing the new token and its scopes](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/vimeo-personal-access-token.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=73a1065cfeac91eaaccc874adf5e63a2) **4. Preview the migration with a dry run** Run the import with `--dry-run`, using the library ID from step 2: ```sh theme={null} bunny stream import --library 12345 --source vimeo --dry-run ``` `--dry-run` prints the import plan without making any changes to either platform. The CLI then prompts for your Vimeo access token. Paste it at the **Access Token** prompt: ```sh theme={null} Vimeo credentials https://developer.vimeo.com/apps (scopes: public, private, video_files) ✔ Access Token: … ******************************** ``` Once Vimeo accepts the token, the CLI offers to save it so later runs skip this prompt. Press Enter to accept: ```sh theme={null} ✔ Save Vimeo credentials for next time? … yes ✓ Saved Vimeo credentials to the "default" profile. ``` > [accordion] Where the CLI stores credentials > > The CLI stores saved credentials in `~/.config/bunnynet/stream-import-credentials.json` (or under `$XDG_CONFIG_HOME/bunnynet/` when it is set). Only your user account can read the file, and it is separate from the main Bunny CLI config. Saved credentials belong to the active Bunny CLI profile. If Vimeo later rejects them, for example after you revoke the token, the CLI deletes them and prompts again on the next run. If you answer no, the CLI prompts on every run. > > The `VIMEO_ACCESS_TOKEN` environment variable always takes priority over a saved token. The dry run then prints a summary and the import plan: ```sh theme={null} Vimeo to Bunny Stream Key Value Library migrate-from-vimeo (12345) Source Vimeo: 6 videos in 3 folders Already imported 0 To import 6 Duration 1:06 Size 21 MB Import plan Product demos (3 videos) - Dashboard tour - Setting up your first project - Inviting your team Webinars (2 videos) - Q3 product update - Customer panel: scaling video (no collection) (1 videos) - Company intro ℹ Dry run. Remove --dry-run to import for real. ``` **Already imported** counts videos that are already in the library, and **To import** counts the ones a real run would import. The size comes from Vimeo's download links, so if the summary has no Size row, check that your plan allows video file access and that the token has the Video Files scope. The plan groups videos by the collection they will land in. Each Vimeo folder maps to a collection of the same name, and videos outside any folder are listed under (no collection). Bunny Stream accepts sources up to 2160p (3840x2160, or 2160x3840 in portrait) and up to 72 hours long, while Vimeo hosts videos up to 8K. The dry run warns about any video beyond those limits, as it will fail on Bunny: ```sh theme={null} ⚠ 1 videos are beyond Bunny's limits and will fail: ⚠ - Drone flyover: 7680x4320 is above Bunny's 2160p limit ``` **5. Run the import** Run the same command without `--dry-run`: ```sh theme={null} bunny stream import --library 12345 --source vimeo ``` The CLI prints the same summary, then asks for confirmation. Type `y` and press Enter to start: ```sh theme={null} ? Import 6 videos into migrate-from-vimeo? › (y/N) ``` The CLI creates a collection for each folder, skips videos that are already imported, hands the rest to Bunny three at a time, and tags each video with `vimeoId`, along with its Vimeo description and tags. It returns once every video is queued. Bunny then fetches each file directly from Vimeo and encodes it. ```sh theme={null} ✔ Import 6 videos into migrate-from-vimeo? … yes ✓ Queued 6 videos into migrate-from-vimeo. Bunny is fetching and encoding them now. ℹ Check progress with bunny stream import status --library 12345 --source vimeo ``` Collection names match without regard to case, so Vimeo folders named `Webinars` and `webinars` land in one collection. Rename one of them in Vimeo before the import to keep them separate. The import command also accepts these flags: * `--wait` keeps the command running, with a live encoding progress indicator, until Bunny has encoded every video. * `--force` skips the confirmation prompt and the offer to retry failed videos, for unattended or CI runs. * `--concurrency N` sets how many videos the CLI passes to Bunny in parallel, from 1 to 20. * `--clean-failed` deletes the failed Bunny copies of videos before importing them again, including videos that Bunny rejected. * `--retry-stalled` deletes and imports again any video stuck at zero bytes on Bunny. * `--request-timeout` sets how many seconds one request to Vimeo or Bunny can take before it gives up (default 30). * `--video-timeout` sets how many seconds the hand-off of one video to Bunny, or with `--wait` its encoding, can take before it gives up (default 5400). Run `bunny stream import --help` for the full list. To migrate a large account one folder at a time, list the folders and their IDs: ```sh theme={null} bunny stream import list --source vimeo ``` ```sh theme={null} ID Name Videos 18273645 Product demos 3 18273699 Webinars 2 1 videos are not in a folder. Import one folder with bunny stream import --source vimeo --folder ``` Preview a folder's videos with `--folder`, then pass the same ID to the import: ```sh theme={null} bunny stream import list --source vimeo --folder 18273645 bunny stream import --library 12345 --source vimeo --folder 18273645 ``` The import works the same way, limited to that folder's videos and its collection. **6. Watch encoding progress** The import returns before Bunny finishes encoding. Check progress with the status command: ```sh theme={null} bunny stream import status --library 12345 --source vimeo ``` It prints one row per queued video, with its status on Bunny, encode percentage, size, and when it was queued: ```sh theme={null} Vimeo to migrate-from-vimeo Video Bunny status Encoded Size Queued Dashboard tour Transcoding 55% 1m ago Setting up your first project Transcoding 55% 1m ago Inviting your team Transcoding 5% 1m ago Q3 product update Finished 100% 26 MB 1m ago Customer panel: scaling video Transcoding 65% 1m ago Company intro Transcoding 55% 1m ago ℹ 1 finished, 5 processing, 0 failed. ``` Run it again until the last line reports 0 processing. The command exits with a non-zero code when any video has failed, so you can also use it as a check in scripts. **7. Verify in the bunny.net dashboard** Open the [bunny.net dashboard](https://dash.bunny.net) and go to **Stream > Your library > Manage library**. Each Vimeo folder appears as a collection, and every migrated video is listed under **Latest videos**. ![The Manage library page in the bunny.net dashboard showing the imported collections and videos](https://mintcdn.com/bunnynet-cb9733c2/ZkttdKBCsq-yq0qO/images/stream/migrate-from-vimeo/manage-library-videos.png?fit=max\&auto=format\&n=ZkttdKBCsq-yq0qO\&q=85\&s=4a9f86aff5a81c0216f751f0ade231ed) Set up the library's [security settings](https://bunny.net/docs/stream/security-options.md) if you have not already, then replace the Vimeo embeds (player.vimeo.com iframes) and links in your site with their Bunny Stream equivalents. See [Embedding videos](https://bunny.net/docs/stream/embedding.md) for the embed and player options. ## Troubleshooting > [accordion] Some videos failed during the import > > When videos fail during an interactive import, the CLI lists each failure and offers to retry them: > > ```sh theme={null} > ✖ Company intro: No download available: the video was not found at the source or has no usable file > ℹ Resume with bunny stream import --library 12345 --source vimeo --resume > ? 1 videos failed. Retry them now? › (y/N) > ``` > > Answer `y` to retry them in the same run. If the import was interrupted, or you declined the retry, run it again with `--resume`: > > ```sh theme={null} > bunny stream import --library 12345 --source vimeo --resume > ``` > > `--resume` retries failed videos, processes any that were still outstanding, and re-applies the `vimeoId` tag to videos Bunny fetched before the run stopped. When the interrupted run was limited to one folder, the resume continues that folder. It stops retrying a video after three failed attempts and skips videos that Bunny rejected. To retry those too, add `--clean-failed`, which deletes their Bunny copies first. > > Running the import again is always safe. Videos that already carry the `vimeoId` tag are skipped, and the CLI reports **Everything is already imported** once nothing is left. > [accordion] No download available > > Vimeo returned no downloadable file for that video. This happens when your Vimeo plan does not allow video file access, when the token is missing the Video Files scope, or when the video was deleted from Vimeo after the import started. > [accordion] Invalid Vimeo access token > > ```sh theme={null} > ✖ Invalid Vimeo access token. > Create one at https://developer.vimeo.com/apps with the public, private and video_files scopes. > ``` > > Vimeo rejected the token. Check that you copied the whole value, then run the command again to re-enter it. > [accordion] Videos are stuck with no data > > If `bunny stream import status` reports videos stuck at zero bytes, run the import again with `--retry-stalled` to delete and fetch them again. This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.