Short
videosPOST /api/v1/videosTopic-to-vertical video with narration and synced captions. Use an Idempotency-Key.
Create Short and Long-form videos, upload and dub or caption a Video Remix, follow every worker lifecycle, and download private MP4s with one Bearer API.
Start here
API access is included with Creator ($9.99/month) and Pro ($14.99/month). Free and Basic plans do not include API access.
Your account has one active key. It is displayed once, expires after 365 days, and regeneration immediately revokes the previous key.
Store the key only in the server-side VIDIPROMPT_API_KEY environment variable. Never expose it in browser code, a URL, cookie, log, screenshot, or AI prompt.
Dashboard and API work share three independent server-enforced meters: Short videos, Long-form videos, and Remix minutes. Check GET /usage before creating work.
https://vidiprompt.com/api/v1export VIDIPROMPT_API_KEY='paste-the-one-time-key-here'Authorization: Bearer $VIDIPROMPT_API_KEYQuickstart
Choose your language and run the complete server-side example. cURL uses only Bash, Python's standard library, and cURL; no VidiPrompt CLI is required.
Store the one-time key only in the VIDIPROMPT_API_KEY server environment variable.
Read all three independent meters and current workflow controls before creating work.
Create a Short job, a Long-form job, or a private resumable Remix upload.
Keep the returned ID and links.self; every worker lifecycle is asynchronous.
Honor Retry-After, upload offsets, project revisions, and idempotency replay headers.
Use the same Bearer key with the shared private download endpoint after completion.
#!/usr/bin/env bash
set -euo pipefail
: "${VIDIPROMPT_API_KEY:?Set VIDIPROMPT_API_KEY before running this script}"
BASE_URL="https://vidiprompt.com/api/v1"
IDEMPOTENCY_KEY="$(python3 -c 'import uuid; print(uuid.uuid4())')"
curl --fail-with-body --silent --show-error \
--request POST "$BASE_URL/videos" \
--header "Authorization: Bearer $VIDIPROMPT_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data @- \
--dump-header response.headers \
--output video.json <<'JSON'
{
"topic": "The emperor who erased his own brother from history",
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
}
JSON
VIDEO_ID="$(python3 -c 'import json; print(json.load(open("video.json"))["id"])')"
VIDEO_URL="$(python3 -c 'import json; print(json.load(open("video.json"))["links"]["self"])')"
printf 'Created video %s\n' "$VIDEO_ID"
while true; do
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $VIDIPROMPT_API_KEY" \
--dump-header response.headers \
--output video.json \
"$VIDEO_URL"
STATUS="$(python3 -c 'import json; print(json.load(open("video.json"))["status"])')"
printf 'Status: %s\n' "$STATUS"
case "$STATUS" in
completed) break ;;
queued|processing)
RETRY_AFTER="$(python3 -c 'import re; data=open("response.headers").read(); match=re.search(r"(?im)^Retry-After:\s*(\d+)", data); print(match.group(1) if match else 5)')"
sleep "$RETRY_AFTER"
;;
*)
printf 'Video ended with status %s. See video.json for details.\n' "$STATUS" >&2
exit 1
;;
esac
done
DOWNLOAD_URL="$(python3 -c 'import json; print(json.load(open("video.json"))["links"]["download"])')"
curl --fail-with-body --location --continue-at - \
--header "Authorization: Bearer $VIDIPROMPT_API_KEY" \
--output "$VIDEO_ID.mp4" \
"$DOWNLOAD_URL"
printf 'Saved %s.mp4\n' "$VIDEO_ID"
import { randomUUID } from 'node:crypto';
import { writeFile } from 'node:fs/promises';
const apiKey = process.env.VIDIPROMPT_API_KEY;
if (!apiKey) throw new Error('Set VIDIPROMPT_API_KEY before running this script.');
const baseUrl = 'https://vidiprompt.com/api/v1';
const authHeaders = { Authorization: 'Bearer ' + apiKey };
const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));
async function readJson(response) {
const data = await response.json();
if (!response.ok) {
throw new Error(data.detail || data.title || 'VidiPrompt API request failed');
}
return data;
}
let response = await fetch(baseUrl + '/videos', {
method: 'POST',
headers: {
...authHeaders,
'Content-Type': 'application/json',
'Idempotency-Key': randomUUID(),
},
body: JSON.stringify({
"topic": "The emperor who erased his own brother from history",
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
}),
});
let video = await readJson(response);
let retryAfter = Number(response.headers.get('Retry-After') || 5);
console.log('Created video', video.id);
while (video.status === 'queued' || video.status === 'processing') {
await sleep(retryAfter);
response = await fetch(video.links.self, { headers: authHeaders });
video = await readJson(response);
retryAfter = Number(response.headers.get('Retry-After') || 5);
console.log('Status:', video.status);
}
if (video.status !== 'completed' || !video.links.download) {
throw new Error('Video ended with status ' + video.status + '.');
}
const download = await fetch(video.links.download, { headers: authHeaders });
if (!download.ok) throw new Error('Download failed with HTTP ' + download.status + '.');
await writeFile(video.id + '.mp4', Buffer.from(await download.arrayBuffer()));
console.log('Saved', video.id + '.mp4');
import os
import time
import uuid
from pathlib import Path
import requests
api_key = os.environ.get("VIDIPROMPT_API_KEY")
if not api_key:
raise RuntimeError("Set VIDIPROMPT_API_KEY before running this script.")
base_url = "https://vidiprompt.com/api/v1"
auth_headers = {"Authorization": f"Bearer {api_key}"}
response = requests.post(
f"{base_url}/videos",
headers={
**auth_headers,
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"topic": "The emperor who erased his own brother from history",
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
},
timeout=30,
)
response.raise_for_status()
video = response.json()
retry_after = int(response.headers.get("Retry-After", "5"))
print("Created video", video["id"])
while video["status"] in {"queued", "processing"}:
time.sleep(retry_after)
response = requests.get(video["links"]["self"], headers=auth_headers, timeout=30)
response.raise_for_status()
video = response.json()
retry_after = int(response.headers.get("Retry-After", "5"))
print("Status:", video["status"])
if video["status"] != "completed" or not video["links"].get("download"):
raise RuntimeError(f"Video ended with status {video['status']}.")
output = Path(f"{video['id']}.mp4")
with requests.get(video["links"]["download"], headers=auth_headers, stream=True, timeout=60) as download:
download.raise_for_status()
with output.open("wb") as file:
for chunk in download.iter_content(chunk_size=1024 * 1024):
if chunk:
file.write(chunk)
print("Saved", output)
POST /api/v1/videosTopic-to-vertical video with narration and synced captions. Use an Idempotency-Key.
POST /api/v1/long-form/videosAuthorized 3,000–60,000 character script to an 11–15 minute horizontal video.
POST /api/v1/remix/uploadsResumable private upload, captions-only or optional TTS, original-audio controls, then export.
The canonical Markdown contains complete terminal-ready Short, Long-form, and resumable Remix scripts. The OpenAPI contract contains every request schema, mutation, status, and error.
202 Accepted · Retry-After: 5{
"id": "123e4567-e89b-42d3-a456-426614174000",
"object": "video",
"workflow": "short",
"status": "queued",
"created_at": "2026-08-13T18:00:00.000Z",
"queue": {
"position": 2
},
"settings": {
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
},
"usage": {
"used": 3,
"reserved": 1,
"remaining": 11,
"limit": 15
},
"links": {
"self": "https://vidiprompt.com/api/v1/videos/123e4567-e89b-42d3-a456-426614174000",
"download": null
}
}Keep id and links.self. The usage object reflects the shared allowance after this job is reserved.
status: completed{
"id": "123e4567-e89b-42d3-a456-426614174000",
"object": "video",
"workflow": "short",
"status": "completed",
"source": "api",
"created_at": "2026-08-13T18:00:00.000Z",
"updated_at": "2026-08-13T18:04:00.000Z",
"expires_at": "2026-08-20T18:00:00.000Z",
"settings": {
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
},
"progress": {
"percent": 100,
"stage": "completed",
"detail": "Complete!"
},
"output": {
"duration_seconds": 42.5
},
"links": {
"self": "https://vidiprompt.com/api/v1/videos/123e4567-e89b-42d3-a456-426614174000",
"download": "https://vidiprompt.com/api/v1/videos/123e4567-e89b-42d3-a456-426614174000/download"
}
}Fetch links.download with the same Bearer header before expires_at. Completed outputs are retained for 7 days.
Job lifecycle
All three workflows are asynchronous. Creation or upload completion returns an owner-scoped resource to poll and the same protected final-download contract.
Create or upload
Choose workflowWaiting
queuedWorker lifecycle
processingReady
completedMP4
Authenticated downloadfailed, cancelled, and expired do not produce a downloadable file. Stop polling and handle the final status. Long-form and Remix also expose safe cancellation operations.API reference
All paths below are relative to https://vidiprompt.com/api/v1. Send the API key as a Bearer token on every request.
/videosIdempotently reserve one Short video and queue generation.
202 Accepted · Short resource, usage reservation, Location, and Retry-After./long-form/videosIdempotently reserve the independent Long-form meter and queue an 11–15 minute video.
202 Accepted · Long-form resource, phase, allowance, Location, and Retry-After./long-form/videos/{video_id}Read phase, progress, safe settings, output, and links.
200 OK · Retry-After until terminal./long-form/videos/{video_id}/cancelCancel queued work or request cancellation from the active Long-form worker.
200 OK · Idempotent cancellation state; unfinished quota is released./remix/uploadsCreate a private resumable upload after paid-plan and remaining-minute checks.
201 Created · Upload/video IDs, 8 MiB chunk size, durable offset, and 24-hour expiry./remix/uploads/{upload_id}Read the server offset, append one exact chunk, or abandon and clean staging data.
200 / 204 · Durable Upload-Offset/Length/Expires headers; mismatches return 409./remix/uploads/{upload_id}/completePublish the source privately, probe trusted duration/media, meter ceil minutes, and queue analysis.
202 Accepted · Analysis state; concurrent exact completion is replay-safe./remix/projects/{video_id}Control framing, aspect, captions, original audio, optional TTS replace/mix, volumes, and ducking.
200 OK · Incremented revision and private API media links./remix/projects/{video_id}/captionsAtomically install non-overlapping custom caption cues.
200 OK · Updated project with incremented revision./remix/projects/{video_id}/audio-previewGenerate or reuse TTS and final audio mixing before export.
200 OK · Private mix URL. First successful TTS is included; changed uncached TTS consumes source minutes again./remix/projects/{video_id}/voiceover-draftCreate a voiceover script from the private source transcript.
200 OK · Updated project; no provider output or secrets are exposed./remix/projects/{video_id}/translate-captionsTranslate analyzed captions while preserving timings.
200 OK · Custom translated cues and incremented revision./remix/projects/{video_id}/exportFreeze the exact project revision and queue the existing Remix worker.
202 Accepted · Queued resource and Remix-minute state; concurrent replay does not double-charge./remix/projects/{video_id}/cancelCancel upload, analysis, draft, or queued/in-progress export safely.
200 OK · Idempotent cancelled state; consumed source minutes are not refunded./remix/projects/{video_id}/media/{asset}Stream owner-scoped source, thumbnail, voiceover, or mix media with bounded byte ranges.
200 / 206 · Private no-store media; foreign and malformed resources are indistinguishable./videosList Short, Long-form, and Remix resources with signed cursor pagination.
200 OK · Stable workflow-aware resources and next_cursor./videos/{video_id}Read the current public status for any owned workflow.
200 OK · Safe workflow settings and links; no storage paths or provider errors./videos/{video_id}/downloadDownload a completed output from any workflow using trusted private storage resolution.
200 / 206 · Private no-store MP4, ETag, HEAD, resume, and 416 support./usageRead independent Short videos, Long-form videos, and Remix minutes.
200 OK · Units, used/reserved/remaining/limit, exact window, and legacy Short fields./capabilitiesDiscover all current catalogs, workflow controls, sizes, durations, and plan limits.
200 OK · Account-aware source of truth; do not hardcode values.Unknown fields are rejected. The complete JSON body must be no larger than 32 KiB.
topicstringvideo_modestringstyle_packstringvoicestringlanguagestringcaptionsobjectFetch GET /capabilities instead of hardcoding modes, styles, voices, languages, caption options, or defaults. Use the OpenAPI contract for exhaustive schemas.
{
"topic": "The emperor who erased his own brother from history",
"video_mode": "darkHistory",
"style_pack": "documentary",
"voice": "warm-storyteller",
"language": "en",
"captions": {
"style": "tiktok",
"position": "center"
}
}Errors and retries
Errors use an RFC 9457 application/problem+json body. Branch on the stable code, keep request_id for support, and use bounded backoff only when retrying is appropriate.
Example problem response
{
"type": "https://vidiprompt.com/problems/quota-exceeded",
"title": "Video allowance exhausted",
"status": 403,
"code": "quota_exceeded",
"detail": "Your current video allowance is exhausted.",
"instance": "/api/v1/videos",
"request_id": "00000000-0000-4000-8000-000000000001"
}Honor Retry-After on polling, 429, and retryable 503 responses. Do not blindly retry validation, permission, quota, conflict, or not-found errors.
| Code | HTTP | What to do |
|---|---|---|
| invalid_api_key | 401 | Use the current Bearer key; cookies and alternate key headers are never accepted. |
| api_access_required | 403 | API access requires a current Creator or Pro entitlement. |
| quota_exceeded | 403 | The independent Short allowance is exhausted. |
| long_form_quota_exceeded | 409 | Wait for the billing reset or upgrade; do not change the Short or Remix meter. |
| remix_quota_exceeded | 403 | Not enough Remix minutes remain for the trusted source duration or changed TTS. |
| offset_mismatch | 409 | HEAD the upload, then resume exactly at Upload-Offset. |
| revision_conflict | 409 | GET the project and retry intentionally with its current full settings snapshot. |
| operation_in_progress | 409 | Wait and retry; provider-operation leasing prevents duplicate paid work. |
| idempotency_required | 400 | Send a 16–128 visible ASCII Idempotency-Key for Short and Long-form creates. |
| idempotency_conflict | 409 | The key was used with different normalized input; use a new key. |
| rate_limited | 429 | Wait for Retry-After and retry the same safe operation. |
| video_not_ready | 409 | Poll until completed before downloading. |
| video_not_found | 404 | Malformed, missing, expired, and cross-tenant IDs share this response. |
| service_unavailable | 503 | Retry with bounded backoff and reuse the same idempotency key/input. |
Limits and security
There is no separate API allowance. Short videos, Long-form videos, and Remix minutes are independent server-authoritative meters shared across dashboard and API.
Short
Free 1/7d · Basic 7 · Creator 15 · Pro 35
Videos; dashboard and API share one server-authoritative meter.
Long-form
0 · 2 · 5 · 8
Videos per billing period; one outstanding Long-form job at a time.
Remix
0 · 30 · 60 · 120
Minutes per billing period; trusted duration rounds up, 10 minute maximum.
API eligibility
Creator and Pro
The three plan meters are shared with the dashboard; API is not a separate allowance.
Upload
300 MB · 8 MiB chunks
MP4, MOV, or WebM; resumable for 24 hours.
Retention
7 days
Download private completed MP4 files before expires_at.
Idempotency
24 hours
Exact Short/Long creates replay; Remix completion/export/TTS are state-idempotent.
Active keys
1 per account
Regeneration immediately revokes the previous key.
Key lifetime
365 days
Generate a replacement before expiry.
Send it only as Authorization: Bearer $VIDIPROMPT_API_KEY. Never expose it in browser code, URLs, query strings, cookies, screenshots, logs, or AI prompts. VidiPrompt stores only a one-way hash, and regenerating the account's one active key immediately revokes the previous key.
AI integration
The Copy for an LLM action copies the same canonical Markdown served publicly, without your key, email, usage, or account identifiers. The integration prompt gives an AI a safe ten-step workflow.
Paste the copied material into your AI assistant, then provide your application requirements separately. Never paste the API key itself.