Bunnyx

CIHex.pmDocs

Elixir client for the bunny.net API. Built on Req.

Covers the full bunny.net platform — CDN, edge storage, S3-compatible storage, DNS, video streaming, Shield/WAF, edge scripting, magic containers, billing, and more.

Installation

def deps do
  [
    {:bunnyx, "~> 0.4"}
  ]
end

Quick start

Create a client and pass it to every call. No global config needed — multiple clients with different credentials work side by side.

client = Bunnyx.new(api_key: "sk-...")

# CDN — returns a %Bunnyx.PullZone{} struct
{:ok, zone} = Bunnyx.PullZone.create(client, %{name: "my-zone", origin_url: "https://example.com"})
zone.id      #=> 12345
zone.name    #=> "my-zone"

# DNS
{:ok, dns} = Bunnyx.DnsZone.create(client, %{domain: "example.com"})
{:ok, record} = Bunnyx.DnsRecord.add(client, dns.id, %{type: 0, name: "www", value: "1.2.3.4", ttl: 300})

# Keyword lists work too
Bunnyx.PullZone.create(client, name: "my-zone", origin_url: "https://example.com")

# Typos fail fast
Bunnyx.PullZone.create(client, %{nme: "oops"})
#=> ** (ArgumentError) unknown key :nme. Valid keys: :name, :origin_url, ...

Clients

bunny.net uses different authentication for different services. Bunnyx provides four client types:

Client Auth Use for
Bunnyx.new/1 Account API key CDN, DNS, storage zones, video libraries, Shield, billing, and everything else
Bunnyx.Storage.new/1 Storage zone password File upload, download, delete, list
Bunnyx.S3.new/1 Zone name + password (SigV4) S3-compatible storage with multipart uploads
Bunnyx.Stream.new/1 Library API key Video CRUD, upload, collections, captions
# Edge storage — upload and download files
storage = Bunnyx.Storage.new(storage_key: "pw-...", zone: "my-zone")
{:ok, nil} = Bunnyx.Storage.put(storage, "/images/logo.png", image_data)
{:ok, data} = Bunnyx.Storage.get(storage, "/images/logo.png")

# S3-compatible storage
s3 = Bunnyx.S3.new(zone: "my-zone", storage_key: "pw-...", region: "de")
{:ok, nil} = Bunnyx.S3.put(s3, "file.txt", "hello")
{:ok, result} = Bunnyx.S3.list(s3, prefix: "images/")

# Video streaming
stream = Bunnyx.Stream.new(api_key: "lib-key-...", library_id: 12345)
{:ok, video} = Bunnyx.Stream.create(stream, %{title: "My Video"})
{:ok, nil} = Bunnyx.Stream.upload(stream, video.guid, video_binary)

API coverage

Main API (Bunnyx.new/1)

Separate clients

Error handling

API errors return {:ok, result} or {:error, %Bunnyx.Error{}}. Errors include the HTTP method and path for debugging:

case Bunnyx.PullZone.get(client, 999) do
  {:ok, zone} -> zone
  {:error, %Bunnyx.Error{status: 404}} -> nil
  {:error, error} -> raise "#{error.method} #{error.path}: #{error.message}"
end

Invalid arguments (unknown keys, typos) raise ArgumentError before any HTTP call.

Design

Bunnyx follows Elixir library conventions — no compile-time config, no global state, explicit clients.

Integration testing

The livebooks/ directory contains per-domain integration tests that exercise every SDK function against the real bunny.net API. Set LB_BUNNY_API_KEY and run the cells.

License

MIT — see LICENSE.

LLM disclosure

The creation of this library was expedited by LLMs, a mix of local and remote models, with strict human supervision. The surface area of the bunny.net API is large and it made the creation of this library feasible. The code and library design follows my own preferences and what I consider to be clean, best practice Elixir code.