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:
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.
| Field | Type | Notes |
|---|---|---|
style | string | A style id, for example low-poly. List them with GET /v1/styles or browse the styles. |
prompt | string | 3 to 2,000 characters. Put the exact on-screen words and numbers in it. |
duration_s | integer | 5 to 40, limited by your plan. |
aspect | string | 9:16 (1080 × 1920), 16:9 (1920 × 1080), 1:1 (1080 × 1080) |
tier | string | standard or pro. Pro costs 3× the credits. |
options.captions | boolean | Default true. |
options.language | string | Caption language as a tag such as en or pt-BR. Default en. |
options.sound | boolean | Default true. |
options.palette | string[] | 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.
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.
| Length | Standard | Pro |
|---|---|---|
| 5–10 s | 350 | 1,050 |
| 11–20 s | 500 | 1,500 |
| 21–30 s | 700 | 2,100 |
| 31–40 s | 900 | 2,700 |
The job object
| Field | Notes |
|---|---|
id | Use it to poll, cancel and delete. |
status | queued, running, succeeded, failed, canceled |
progress | { phase, pct, message }. Phases, in order: queued, checking, planning, building, rendering, finishing, done. |
style, prompt, duration_s, aspect, tier, options | What you sent, after defaults. |
cost_credits | The fixed price of this job. |
created_at, started_at, finished_at | ISO 8601 timestamps; the last two appear when they happen. |
video_url, poster_url | Present once the job has succeeded and the files still exist. |
width, height, bytes | Of the finished video. |
expires_at | When 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 }.
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
| Request | Returns |
|---|---|
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/:id | 204, 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/me | Your plan, credit balance and limits. |
Errors
Errors use the HTTP status and one shape: { "error": { "code": "…", "message": "…" } }.
| Status | Code | When |
|---|---|---|
| 401 | unauthenticated | The key is missing or wrong. |
| 402 | no_subscription, insufficient_credits | No active plan, or not enough credits for this job. |
| 403 | api_not_in_plan, tier_not_in_plan, duration_not_in_plan, account_suspended | The plan does not allow the request. |
| 404 | not_found | No such job, or it belongs to another account. |
| 409 | generation_disabled, job_active | Generation is paused for everyone, or you tried to delete a job that is still active. |
| 413 | payload_too_large | The body is over 16 KB. |
| 422 | invalid_input, prompt_rejected | A field is wrong, or the prompt was refused by the pre-flight check. Nothing is charged. |
| 429 | rate_limited, too_many_running_jobs, too_many_failed_jobs, daily_limit_reached | Slow down. rate_limited comes with a Retry-After header. |
| 503 | queue_unavailable | Try again shortly. |
Limits by plan
| Plan | API | Requests / min | Jobs / hour | At a time | Max length | Tiers |
|---|---|---|---|---|---|---|
| Starter | No | n/a | 6 | 1 | 20 s | standard |
| Creator | Yes | 60 | 15 | 2 | 40 s | standard, pro |
| Studio | Yes | 180 | 40 | 4 | 40 s | standard, pro |
Node and Python
// 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# 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.