[Docs] API v1

API reference: one POST, then poll.

The API takes the same brief as the app and returns the same job object. It is part of the Creator and Studio plans. Base URL: https://agentvideo.app/v1

Authentication

Create a key in the app under API keys. The secret starts with av_live_ and is shown once; only a hash is stored. Send it on every request:

Header
Authorization: Bearer av_live_…

All requests and responses are JSON. Requests without a valid key get 401 unauthenticated; a key on a plan without API access gets 403 api_not_in_plan; an account without an active subscription gets 402 no_subscription.

Create a job

POST /v1/jobs starts one video. The body may be at most 16 KB. Unknown fields are rejected.

FieldTypeNotes
stylestringA style id, for example low-poly. List them with GET /v1/styles or browse the styles.
promptstring3 to 2,000 characters. Put the exact on-screen words and numbers in it.
duration_sinteger5 to 40, limited by your plan.
aspectstring9:16 (1080 × 1920), 16:9 (1920 × 1080), 1:1 (1080 × 1080)
tierstringstandard or pro. Pro costs 3× the credits.
options.captionsbooleanDefault true.
options.languagestringCaption language as a tag such as en or pt-BR. Default en.
options.soundbooleanDefault true.
options.palettestring[]Optional. 1 to 6 colours as #rrggbb. Each style decides which of its colours they replace.

Send an Idempotency-Key header (1 to 128 printable ASCII characters) and a repeated POST returns the first job instead of starting a second one.

Create a job
curl https://agentvideo.app/v1/jobs \
  -H "Authorization: Bearer $AGENTVIDEO_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: release-2.4-clip" \
  -d '{
    "style": "low-poly",
    "prompt": "A paper boat crosses a storm and reaches a lighthouse",
    "duration_s": 15,
    "aspect": "9:16",
    "tier": "standard",
    "options": { "captions": true, "language": "en", "sound": true }
  }'

# 202
# { "job": { "id": "…", "status": "queued", "cost_credits": 500,
#            "progress": { "phase": "queued", "pct": 0, "message": "…" }, … } }

Submitting reserves the price in credits. A finished video is charged. If the job fails on our side, or the prompt is refused before the agent starts, the reservation is released in full. A job you cancel after the agent has started is charged.

Credits per video
LengthStandardPro
5–10 s3501,050
11–20 s5001,500
21–30 s7002,100
31–40 s9002,700

The job object

FieldNotes
idUse it to poll, cancel and delete.
statusqueued, running, succeeded, failed, canceled
progress{ phase, pct, message }. Phases, in order: queued, checking, planning, building, rendering, finishing, done.
style, prompt, duration_s, aspect, tier, optionsWhat you sent, after defaults.
cost_creditsThe fixed price of this job.
created_at, started_at, finished_atISO 8601 timestamps; the last two appear when they happen.
video_url, poster_urlPresent once the job has succeeded and the files still exist.
width, height, bytesOf the finished video.
expires_atWhen the files are deleted. It depends on the plan.
error{ code, message } on a failed job.

Poll for the result

GET /v1/jobs/:id returns the job. A video takes minutes, not seconds, so poll every 10 seconds or so. For a finer log, GET /v1/jobs/:id/events?after=<seq> returns { events: [{ seq, at, phase, message }], status }.

Poll and download
curl https://agentvideo.app/v1/jobs/$JOB_ID \
  -H "Authorization: Bearer $AGENTVIDEO_KEY"

# while it runs
# { "job": { "status": "running", "progress": { "phase": "rendering", "pct": 72, "message": "…" } } }
# when it is done
# { "job": { "status": "succeeded", "video_url": "https://agentvideo.app/media/…/video.mp4?t=…",
#            "poster_url": "https://agentvideo.app/media/…/poster.jpg?t=…",
#            "width": 1080, "height": 1920, "expires_at": "…" } }

curl -L -o video.mp4 "$VIDEO_URL"

Result files

video_url is an H.264 mp4 at 30 fps and poster_url a JPEG frame. Both are absolute, signed links that work for one hour without a key. Fetch the job again to get fresh links. The files stay until expires_at: 30 days on Starter, 90 days on Creator, 180 days on Studio.

Other endpoints

RequestReturns
GET /v1/jobs?limit=20&cursor=…{ jobs, next_cursor }, newest first. limit is 1 to 50.
POST /v1/jobs/:id/cancel{ job }. Before the agent starts the credits are released; after, the job is charged.
DELETE /v1/jobs/:id204, and the files are deleted too. A queued or running job answers 409 job_active: cancel it first.
GET /v1/styles{ styles }: id, name, tagline, family, tags and what each style is good for.
GET /v1/meYour plan, credit balance and limits.

Errors

Errors use the HTTP status and one shape: { "error": { "code": "…", "message": "…" } }.

StatusCodeWhen
401unauthenticatedThe key is missing or wrong.
402no_subscription, insufficient_creditsNo active plan, or not enough credits for this job.
403api_not_in_plan, tier_not_in_plan, duration_not_in_plan, account_suspendedThe plan does not allow the request.
404not_foundNo such job, or it belongs to another account.
409generation_disabled, job_activeGeneration is paused for everyone, or you tried to delete a job that is still active.
413payload_too_largeThe body is over 16 KB.
422invalid_input, prompt_rejectedA field is wrong, or the prompt was refused by the pre-flight check. Nothing is charged.
429rate_limited, too_many_running_jobs, too_many_failed_jobs, daily_limit_reachedSlow down. rate_limited comes with a Retry-After header.
503queue_unavailableTry again shortly.

Limits by plan

PlanAPIRequests / minJobs / hourAt a timeMax lengthTiers
StarterNon/a6120 sstandard
CreatorYes6015240 sstandard, pro
StudioYes18040440 sstandard, pro

Node and Python

Node
// Node 18+ (built-in fetch)
const API = 'https://agentvideo.app/v1'
const headers = { Authorization: 'Bearer ' + process.env.AGENTVIDEO_KEY, 'Content-Type': 'application/json' }

const res = await fetch(API + '/jobs', {
  method: 'POST',
  headers,
  body: JSON.stringify({
    style: 'low-poly',
    prompt: 'A paper boat crosses a storm and reaches a lighthouse',
    duration_s: 15,
    aspect: '9:16',
    tier: 'standard',
  }),
})
if (!res.ok) throw new Error((await res.json()).error.message)
let { job } = await res.json()

while (job.status === 'queued' || job.status === 'running') {
  await new Promise((r) => setTimeout(r, 10_000))
  job = (await (await fetch(API + '/jobs/' + job.id, { headers })).json()).job
  console.log(job.progress.phase, job.progress.pct + '%')
}
if (job.status !== 'succeeded') throw new Error(job.error?.message ?? job.status)
console.log(job.video_url) // signed, valid for one hour
Python
# pip install requests
import os, time, requests

API = "https://agentvideo.app/v1"
headers = {"Authorization": "Bearer " + os.environ["AGENTVIDEO_KEY"]}

r = requests.post(API + "/jobs", headers=headers, json={
    "style": "low-poly",
    "prompt": "A paper boat crosses a storm and reaches a lighthouse",
    "duration_s": 15,
    "aspect": "9:16",
    "tier": "standard",
})
if not r.ok:
    raise SystemExit(r.json()["error"]["message"])
job = r.json()["job"]

while job["status"] in ("queued", "running"):
    time.sleep(10)
    job = requests.get(API + "/jobs/" + job["id"], headers=headers).json()["job"]
    print(job["progress"]["phase"], job["progress"]["pct"])

if job["status"] != "succeeded":
    raise SystemExit(job.get("error", {}).get("message", job["status"]))
open("video.mp4", "wb").write(requests.get(job["video_url"]).content)

A machine-readable summary of the product is at /llms.txt.