Build on VideoGenAI.
REST API, idempotent job submission, streaming webhook events, first-class SDKs in JS/Py/Go. Available from Studio tier upwards.
Authorization: Bearer sk_...Your first render in 60 seconds
copy · paste · run# 1. Export your key (get it from the dashboard).
export VGAI_API_KEY=sk_live_...
# 2. Submit a render.
curl -s https://api.videogenai.io/v1/renders \
-H "Authorization: Bearer $VGAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "a golden retriever running on a beach at dusk", "duration_sec": 8 }' \
| tee render.json
# 3. Wait, then fetch.
ID=$(jq -r .id render.json)
while [ "$(curl -s https://api.videogenai.io/v1/renders/$ID \
-H "Authorization: Bearer $VGAI_API_KEY" | jq -r .status)" != "completed" ]; do sleep 2; done
curl -s https://api.videogenai.io/v1/renders/$ID \
-H "Authorization: Bearer $VGAI_API_KEY" | jq -r .urlAuthentication
API keys
Create keys from Settings → API keys. Each key is sk_live_ or sk_test_. Test keys only hit the preview model pool and are free.
- Rotate keys without downtime; old key is valid for 30 minutes after revocation.
- Scope keys to a single project, or grant workspace-wide access.
- Optional IP allowlist per key (CIDR).
SSO & SCIM (Infinite)
Workspaces on Infinite get SAML 2.0 or OIDC sign-in, plus SCIM 2.0 for user provisioning. Org-level API keys can require hardware-backed auth before creation.
Endpoints
/v1/renderscurl https://api.videogenai.io/v1/renders \
-H "Authorization: Bearer $VGAI_API_KEY" \
-H "Idempotency-Key: 0a8f…" \
-H "Content-Type: application/json" \
-d '{
"prompt": "cinematic drone shot over a neon tokyo skyline at dusk",
"resolution": "1080p",
"duration_sec": 30,
"aspect_ratio": "16:9",
"seed": 42,
"format": "mp4",
"model_hint": "auto",
"webhook_url": "https://yourapp.com/hooks/vgai"
}'{
"id": "rnd_01HZ...",
"status": "queued",
"created_at": "2026-04-18T16:12:09Z",
"model_chosen": "kling-3",
"estimated_tokens": 60,
"estimated_usd": 1.89,
"estimated_ready_at": "2026-04-18T16:12:51Z"
}/v1/renders/:idcurl https://api.videogenai.io/v1/renders/rnd_01HZ... \
-H "Authorization: Bearer $VGAI_API_KEY"{
"id": "rnd_01HZ...",
"status": "completed",
"progress": 1.0,
"url": "https://cdn.videogenai.io/renders/rnd_01HZ.../out.mp4",
"thumbnail_url": "https://cdn.videogenai.io/renders/rnd_01HZ.../poster.jpg",
"duration_sec": 30,
"resolution": "1080p",
"tokens_consumed": 60,
"billed_usd": 1.89,
"model_used": "kling-3",
"completed_at": "2026-04-18T16:12:49Z"
}/v1/renderscurl "https://api.videogenai.io/v1/renders?status=completed&limit=20" \
-H "Authorization: Bearer $VGAI_API_KEY"{
"data": [
{ "id": "rnd_01HZ...", "status": "completed", "created_at": "..." },
{ "id": "rnd_01HY...", "status": "completed", "created_at": "..." }
],
"has_more": true,
"next_cursor": "rnd_01HX..."
}/v1/renders/:id/retrycurl -X POST https://api.videogenai.io/v1/renders/rnd_01HZ.../retry \
-H "Authorization: Bearer $VGAI_API_KEY"{
"id": "rnd_01J0...",
"retry_of": "rnd_01HZ...",
"status": "queued"
}/v1/renders/:idcurl -X DELETE https://api.videogenai.io/v1/renders/rnd_01HZ... \
-H "Authorization: Bearer $VGAI_API_KEY"{
"id": "rnd_01HZ...",
"status": "canceled",
"refunded_tokens": 60
}/v1/modelscurl https://api.videogenai.io/v1/models \
-H "Authorization: Bearer $VGAI_API_KEY"{
"data": [
{ "id": "kling-3", "version": "3.0.4", "best_for": ["social", "action"] },
{ "id": "veo-3-1", "version": "3.1.0", "best_for": ["physics", "fluids"] },
{ "id": "runway-4-5", "version": "4.5.2", "best_for": ["narrative"] }
]
}/v1/assetscurl -X POST https://api.videogenai.io/v1/assets \
-H "Authorization: Bearer $VGAI_API_KEY" \
-F "file=@reference.jpg" \
-F "kind=image"{
"id": "ast_01HZ...",
"kind": "image",
"expires_at": "2026-04-19T16:12:09Z",
"url": "https://cdn.videogenai.io/assets/ast_01HZ..."
}/v1/usagecurl "https://api.videogenai.io/v1/usage?period=2026-04&group_by=project" \
-H "Authorization: Bearer $VGAI_API_KEY"{
"period": "2026-04",
"total_tokens": 18420,
"total_usd": 581.23,
"by_project": [
{ "project_id": "prj_ads", "tokens": 11200, "usd": 352.12 },
{ "project_id": "prj_shorts","tokens": 7220, "usd": 229.11 }
]
}/v1/webhookscurl https://api.videogenai.io/v1/webhooks \
-H "Authorization: Bearer $VGAI_API_KEY"{
"data": [
{ "id": "whk_01HZ...", "url": "https://yourapp.com/hooks/vgai",
"events": ["render.completed", "render.failed"], "active": true }
]
}Models & routing
By default every prompt goes through our router and lands on whichever model scores highest for that category on our weekly eval. You can pin a specific model with model_hint, but the cost is fixed to that model's rate.
| id | best for | max res | Route weight |
|---|---|---|---|
| seedance-2-0 | cinematic, stylised | 1080p | 58% |
| seedance-1-5-pro | fast lane, throwaways | 1080p | 21% |
| happy-horse-1-0 | rolling out soon | 1080p | 12% |
| internal-lite | previews | 720p | 9% |
Webhooks
Skip polling. Provide a webhook_url on render create and we'll POST the final payload to your endpoint when the job completes. Every event is signed with HMAC-SHA256 — verify with the shared secret from your dashboard.
POST / HTTP/1.1
Content-Type: application/json
VGAI-Signature: t=1713456789,v1=9d3f...
VGAI-Event: render.completed
VGAI-Delivery: dlv_01HZ...
{
"type": "render.completed",
"data": {
"id": "rnd_01HZ...",
"url": "https://cdn.videogenai.io/renders/rnd_01HZ.../out.mp4",
"tokens_consumed": 60,
"billed_usd": 1.89
}
}| event | when | payload |
|---|---|---|
| render.started | Job has been picked up by a worker. | id, model_chosen, started_at |
| render.progress | Every 10% of progress (10%, 20%, …). | id, progress (0–1), eta_seconds |
| render.completed | Render succeeded and the file is on CDN. | id, url, thumbnail_url, tokens_consumed, billed_usd |
| render.failed | Render failed and tokens were refunded. | id, reason, refunded_tokens |
| render.canceled | Render was canceled via DELETE before completion. | id, canceled_at, refunded_tokens |
| webhook.test | Fired when you press Test in the dashboard. | id, nonce, created_at |
Verifying a signature (Node)
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string) {
const [ts, v1] = header.split(",").map((p) => p.split("=")[1]);
const mac = createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
return timingSafeEqual(Buffer.from(mac, "hex"), Buffer.from(v1, "hex"));
}Rate limits per tier
| tier | api access | req / min | concurrent renders | batch size |
|---|---|---|---|---|
| Spark | no | — | — | — |
| Boost | no | — | — | — |
| Pro | no | — | — | — |
| Studio | yes | 100 / min | 16 | 32 |
| Infinite | yes | 600 / min | 64 | 128 |
Every rate-limited response includes Retry-After and X-RateLimit-Reset. Our SDKs honour both automatically.
Errors
| code | name | meaning |
|---|---|---|
| 400 | bad_request | Malformed body or unsupported parameter combo. |
| 401 | unauthorized | Missing or invalid API key. |
| 402 | payment_required | Insufficient tokens; top up or upgrade tier. |
| 403 | forbidden | Key lacks access to this endpoint (needs Studio+). |
| 404 | not_found | Render, asset or webhook id doesn't exist in this workspace. |
| 409 | conflict | Idempotency-Key reused with a different body. |
| 422 | unprocessable_entity | Prompt blocked by moderation filter. |
| 429 | rate_limited | Exceeded per-minute or per-second limit. Retry after header. |
| 500 | internal | Transient failure on our side. Safe to retry. |
| 503 | unavailable | Maintenance window. Respect Retry-After. |
{
"error": {
"code": "rate_limited",
"message": "Too many renders this minute.",
"docs": "https://videogenai.io/developers#errors",
"request_id": "req_01HZ..."
}
}Every error carries a request_id. Include it when you reach out — we can tail logs in one step.
Idempotency & retries
Idempotency-Key
POST endpoints accept an Idempotency-Key header. Send it on every retry and we'll guarantee exactly one render is queued even if your retry storm triples.
# Safe to retry — same key, same body, same result.
curl https://api.videogenai.io/v1/renders \
-H "Authorization: Bearer $VGAI_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "prompt": "...", "duration_sec": 8 }'Retry policy
- Safe to retry:
408,429,5xx - Do not retry:
4xxother than the ones above. - Back-off: exponential, 250ms base, cap 30s, 5 attempts max. Our SDKs default to this.
Pagination
All list endpoints use cursor pagination. Pass limit (default 20, max 100) and cursor (opaque string from the previous response's next_cursor). Order is always newest-first.
curl "https://api.videogenai.io/v1/renders?limit=50&cursor=rnd_01HX..." \
-H "Authorization: Bearer $VGAI_API_KEY"Versioning & stability
Path prefix (/v1/…). Breaking changes go to /v2 with 12 months overlap.
Marked in responses with VGAI-Beta: 1. Shape may change; we'll email you 30 days before any breaking update.
99.98% rolling uptime. Reach out if you see elevated latency and we'll reply with the root cause within one business day.
Client libraries
JavaScript / TypeScript
npm install @videogenai/sdkWorks in Node 18+, Bun, Deno, and Edge runtimes.
import { VideoGenAI } from "@videogenai/sdk";
const client = new VideoGenAI({ apiKey: process.env.VGAI_API_KEY });
const render = await client.renders.create({
prompt: "slow-motion waves crashing on a pebble beach",
resolution: "1080p",
durationSec: 16,
});
const done = await client.renders.waitFor(render.id);
console.log(done.url);Python
pip install videogenaiPython 3.9+. Asyncio-native; sync wrappers included.
from videogenai import VideoGenAI
client = VideoGenAI(api_key=os.environ["VGAI_API_KEY"])
render = client.renders.create(
prompt="slow-motion waves crashing on a pebble beach",
resolution="1080p",
duration_sec=16,
)
done = client.renders.wait_for(render.id)
print(done.url)Go
go get github.com/videogenai/videogenai-goZero-dep client, context-aware.
client := videogenai.NewClient(os.Getenv("VGAI_API_KEY"))
render, err := client.Renders.Create(ctx, &videogenai.RenderParams{
Prompt: "slow-motion waves crashing on a pebble beach",
Resolution: videogenai.Resolution1080p,
DurationSec: 16,
})| feature | JS | Python | Go |
|---|---|---|---|
| Async / await | yes | yes | yes |
| Streaming progress | yes | yes | beta |
| waitFor helper | yes | yes | no |
| Automatic retries | yes | yes | yes |
| Idempotency key helper | yes | yes | yes |
| Webhook verify helper | yes | yes | yes |
| Assets upload | yes | yes | beta |
| TypeScript types | yes | — | — |
Ship with the API in an afternoon.
API access unlocks on Studio tier. Start on any plan, upgrade when you're ready.