API

One resource: a generation. Create it, poll it, download the mp4. Base URL https://api.highlander.sh — the same routes are served from https://highlander.sh.

Authentication

Every request carries a bearer key from your dashboard. Keys look like hl_live_…, are shown once, and are stored only as a hash — if you lose one, revoke it and make another.

header
Authorization: Bearer hl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

POST /v1/generations

Returns 202 immediately with a job id. Credit is reserved at this point, at $0.02 per second of output video ($0.29 for a full 14.375 s clip), and returned if the job does not produce a video.

request
curl -X POST https://api.highlander.sh/v1/generations \
  -H "Authorization: Bearer $HIGHLANDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Waves break over black volcanic rock at dusk.","frames":345,"steps":5}'
FieldTypeNotes
promptstring, requiredUp to 4000 characters. Describes shot, subject, camera and light.
negative_promptstringOptional. Defaults to empty.
framesint, default 3455..345, and must satisfy frames % 17 == 5 — H3's causal VAE packs 17 frames into 5 latents. 24 fps, so 345 frames is 14.375 s.
stepsint, default 52..50. The deployed schedule is tuned for 5. Steps are where the compute goes, so price scales with them: a step count of n costs (n-1)/4 of the listed per-second rate. The default is 1.0x.
seedint, default 1000Same seed and prompt reproduce the same clip.
width / height1344 / 768Fixed. The pipeline is compiled for one resolution; anything else is rejected rather than silently recompiled.

GET /v1/generations/{id}

Poll every couple of seconds. status moves queued → running → succeeded | failed. A warm worker finishes in ~13.5 s; a cold one loads 144 GB of weights and warms compiled kernels first, which takes several minutes.

response
{
  "id": "0f7c1e2a-...",
  "status": "succeeded",
  "prompt": "A traceur vaults a rooftop ledge...",
  "frames": 345,
  "steps": 5,
  "seed": 1000,
  "width": 1344,
  "height": 768,
  "duration_s": 14.375,
  "cost_usd": 0.29,
  "refunded": false,
  "latency_s": 13.506,
  "realtime_factor": 0.9395,
  "error": null,
  "video": "/v1/generations/0f7c1e2a-.../video",
  "poll": "/v1/generations/0f7c1e2a-..."
}

GET /v1/generations/{id}/video

Streams the mp4 (1344x768, 24 fps, AAC stereo generated in the same pass). Returns 409 until the job has succeeded.

Examples

python
import os, time, requests

BASE = "https://api.highlander.sh"
H = {"Authorization": f"Bearer {os.environ['HIGHLANDER_KEY']}"}

job = requests.post(f"{BASE}/v1/generations", headers=H, json={
    "prompt": "A traceur vaults a rooftop ledge into a roll, tracking shot alongside.",
    "frames": 345,      # 14.375 s
    "steps": 5,
}).json()

while True:
    s = requests.get(f"{BASE}/v1/generations/{job['id']}", headers=H).json()
    if s["status"] in ("succeeded", "failed"):
        break
    time.sleep(2)

if s["status"] == "failed":
    raise SystemExit(s["error"])

mp4 = requests.get(f"{BASE}/v1/generations/{job['id']}/video", headers=H).content
open("out.mp4", "wb").write(mp4)
print(f"{s['latency_s']}s render, {s['realtime_factor']}x realtime, ${s['cost_usd']}")
typescript
const BASE = "https://api.highlander.sh";
const H = { Authorization: `Bearer ${process.env.HIGHLANDER_KEY}`,
            "Content-Type": "application/json" };

const job = await fetch(`${BASE}/v1/generations`, {
  method: "POST",
  headers: H,
  body: JSON.stringify({ prompt: "Waves break over black volcanic rock at dusk." }),
}).then((r) => r.json());

let status;
do {
  await new Promise((r) => setTimeout(r, 2000));
  status = await fetch(`${BASE}/v1/generations/${job.id}`, { headers: H })
    .then((r) => r.json());
} while (status.status === "queued" || status.status === "running");

const mp4 = await fetch(`${BASE}/v1/generations/${job.id}/video`, { headers: H });
await Bun.write("out.mp4", await mp4.arrayBuffer());

Errors

401unauthorizedMissing or revoked key.
402insufficient_creditsThe quote exceeds your balance. Nothing was queued.
422invalid_requestBad frame count, prompt too long, unsupported resolution.
429too_many_active_jobsYou already have 2 generations in flight.
503inference_unavailableInference is switched off or the worker is unreachable. You are not charged.
502upstream_errorThe worker rejected the job. Any reservation is refunded.

Errors are JSON with error and message. Anything that fails to produce a video refunds its reservation, so a retry loop cannot silently drain a balance.

Limits and status

  • 2 concurrent generations per account. The deployment serves one job at a time.
  • — Maximum clip length 14.375 s (345 frames).
  • GET /v1/health is public and reports whether inference is currently enabled and how deep the queue is.
  • GET /v1/config (authenticated) returns the deployed model configuration and your balance.