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.
Authorization: Bearer hl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPOST /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.
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}'| Field | Type | Notes |
|---|---|---|
| prompt | string, required | Up to 4000 characters. Describes shot, subject, camera and light. |
| negative_prompt | string | Optional. Defaults to empty. |
| frames | int, default 345 | 5..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. |
| steps | int, default 5 | 2..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. |
| seed | int, default 1000 | Same seed and prompt reproduce the same clip. |
| width / height | 1344 / 768 | Fixed. 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.
{
"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
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']}")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
| 401 | unauthorized | Missing or revoked key. |
| 402 | insufficient_credits | The quote exceeds your balance. Nothing was queued. |
| 422 | invalid_request | Bad frame count, prompt too long, unsupported resolution. |
| 429 | too_many_active_jobs | You already have 2 generations in flight. |
| 503 | inference_unavailable | Inference is switched off or the worker is unreachable. You are not charged. |
| 502 | upstream_error | The 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/healthis 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.