BunnyCDN: List Files → Clickable Download Doc

Skip the awkward storage web UI — one Storage API call, then Markdown/HTML links your partner can open and download

← Tech  ·  Cloudflare DNS  ·  Docker  ·  IndexNow

When to use this

You have a Bunny Storage zone (example: gfafiles) with a folder full of files (example: podcasts/ — “11 pages” in the web UI). You want a clean list of clickable public download links to share with someone.

Best path: Bunny Storage API lists the directory in one JSON response. Then a small script (or jq) turns that into Markdown/HTML.

Process note: The runnable bits live as scripts/list-bunny-storage.cjs and procedures/bunny-list-storage-files.md in the repo — not only as this Astro page — so agents can execute the procedure without “deploying” secrets into a site page.

1. Get the Storage Zone password (AccessKey)

  1. Open the Bunny dashboard.
  2. Go to Storage → your zone (e.g. gfafiles).
  3. Left sidebar → Access.
  4. Copy the Password — that value is the Storage API AccessKey.

This is not the same as your account-level Bunny API key. Keep it out of git, screenshots, and public pages.

2. List files with curl

curl -s -X GET \
  "https://storage.bunnycdn.com/gfafiles/podcasts/" \
  -H "AccessKey: YOUR_STORAGE_ZONE_PASSWORD"

Response is a JSON array. Typical fields:

[
  {
    "ObjectName": "gfa444-non-yt.mp4",
    "Length": 239234567,
    "LastChanged": "2024-12-31T19:07:05",
    "IsDirectory": false
  }
]

Filenames only (needs jq):

curl -s -X GET \
  "https://storage.bunnycdn.com/gfafiles/podcasts/" \
  -H "AccessKey: YOUR_STORAGE_ZONE_PASSWORD" \
  | jq -r '.[].ObjectName' | sort

If the call fails with a region error, check the zone’s storage hostname (e.g. ny.storage.bunnycdn.com) in the dashboard and use that host instead of storage.bunnycdn.com.

3. Public download URLs (Pull Zone)

Storage API access is private. Public downloads go through the Pull Zone (CDN hostname) connected to that storage zone.

  • Default Bunny host: https://YOUR-PULLZONE.b-cdn.net/podcasts/file.mp4
  • Custom domain (recommended for sharing): https://files.globalfromasia.com/podcasts/file.mp4 — any hostname you attached to the pull zone in Bunny (CNAME → pull zone). Use that hostname as BUNNY_PULL_ZONE; you do not need b-cdn.net.

Path after the hostname must match the storage path (folder + filename). Confirm the pull zone is linked to the same storage zone as in the API call, and that the custom domain shows as active on the pull zone.

export BUNNY_PULL_ZONE='files.globalfromasia.com'   # no https://, no trailing slash
# links become: https://files.globalfromasia.com/podcasts/…

4. Script (download + run)

Download the script from this site: /scripts/list-bunny-storage.cjs (same file as in the repo under scripts/ and public/scripts/). No secrets inside — only your env vars unlock a zone.

# Download: /scripts/list-bunny-storage.cjs  then:

export BUNNY_ACCESS_KEY='…'                    # Storage zone Password (Access tab)
export BUNNY_STORAGE_ZONE='gfafiles'
export BUNNY_PULL_ZONE='files.globalfromasia.com'  # custom host OK — no https://

# A) One folder only (e.g. podcasts):
export BUNNY_PATH='podcasts/'
node list-bunny-storage.cjs --out /tmp/gfa-podcasts-links.md

# B) Entire storage zone (all folders + nested files) — need --recursive:
unset BUNNY_PATH
# (BUNNY_PATH='/' also works; prefer unset — both mean zone root)
node list-bunny-storage.cjs --recursive --out /tmp/gfafiles-full.md

# C) Root level only (no subfolders):
unset BUNNY_PATH
node list-bunny-storage.cjs --out /tmp/gfafiles-root.md

open /tmp/gfafiles-full.md
node list-bunny-storage.cjs --help   # recipes also printed here

Useful flags:

  • --recursiverequired for whole-zone / nested folders (API is one directory per call)
  • --html --out /tmp/links.html — browser-friendly list
  • --json — raw file list (pull zone optional)
  • --help — full usage

Operator checklist (for humans + agents): procedures/bunny-list-storage-files.md in the repo.

4b. Whole zone vs one folder

Goal Settings
One folder (e.g. podcasts only) BUNNY_PATH=podcasts/ — recursive only if that folder has subfolders
Entire storage zone Leave BUNNY_PATH empty (zone root) + --recursive
Root files only (no subfolders) Empty path, no --recursive

Bunny’s list endpoint is per directory. Without --recursive, you only see that folder’s immediate children (files + directory names). With --recursive, the script walks every subdirectory and builds one flat list of download links (paths still include folders, e.g. podcasts/ep1.mp4, images/logo.png).

export BUNNY_ACCESS_KEY='…'
export BUNNY_STORAGE_ZONE='gfafiles'
export BUNNY_PULL_ZONE='files.globalfromasia.com'
unset BUNNY_PATH

node scripts/list-bunny-storage.cjs --recursive \
  --title "gfafiles — full zone" \
  --out /tmp/gfafiles-full.md
# → https://files.globalfromasia.com/… paths in the Markdown

5. Example Markdown output

# Podcast Files

- [gfa444-non-yt.mp4](https://files.globalfromasia.com/podcasts/gfa444-non-yt.mp4) (228.1 MB)
- [gfa444-yt.mp4](https://files.globalfromasia.com/podcasts/gfa444-yt.mp4) (190.4 MB)
- [gfa444.mp3](https://files.globalfromasia.com/podcasts/gfa444.mp3) (42.0 MB)

Share that Markdown (or the HTML export) however you normally send docs — GFAVIP, email, Drive. Recipients just click the links if the pull zone allows public read.

6. Python alternative (one-off)

import requests

ACCESS_KEY = "YOUR_STORAGE_ZONE_PASSWORD"
ZONE = "gfafiles"
PATH = "podcasts/"
PULL_ZONE = "your-pullzone.b-cdn.net"

url = f"https://storage.bunnycdn.com/{ZONE}/{PATH}"
files = requests.get(url, headers={"AccessKey": ACCESS_KEY}).json()

print("# Podcast Download Links\n")
for f in sorted(files, key=lambda x: x["ObjectName"]):
    if not f.get("IsDirectory"):
        name = f["ObjectName"]
        size_mb = f["Length"] / (1024 * 1024)
        link = f"https://{PULL_ZONE}/{PATH}{name}"
        print(f"- [{name}]({link}) ({size_mb:.1f} MB)")

7. Security & hygiene

  • Never commit BUNNY_ACCESS_KEY or paste it into Astro frontmatter.
  • Generated lists of private assets should stay out of the public git history.
  • If links must be restricted, use Bunny token authentication / signed URLs on the pull zone — plain b-cdn.net links are typically public.
  • Rotate the storage password if it was shared in chat by mistake.

Hand this to an agent

You only need to provide (not the password in a public ticket if you can help it):

  1. Storage zone name (e.g. gfafiles)
  2. Folder path (e.g. podcasts/)
  3. Pull zone hostname
  4. Whether subfolders should be included (--recursive)

The agent sets BUNNY_ACCESS_KEY in a local shell (or you run the script), writes /tmp/…-links.md, and returns the file for sharing. No second “specialist” agent required for the happy path.

Related

Comments

Approved comments appear below. Log in once with GFAVIP — it applies across the whole site. GFAVIP login

View comments archive