# fast.video API — full reference for coding agents fast.video turns a prompt, a picture, or reference pictures into a video clip: 720p, 24 fps, with sound, 5 to 15 seconds per clip. It also runs batches (up to 20 clips in one call) and long clips (up to 60 seconds, joined from segments). One price: $0.009 per second of video (GET /v1/pricing for the live number). Failed clips are refunded automatically. Base URL: https://fast.video Human docs: https://fast.video/docs · OpenAPI: https://fast.video/openapi.json ## Rules for agents - Read the API key from the environment variable FAST_VIDEO_KEY. Never write the key into code, logs, commits or front-end code. Never ask the user to paste it into chat. - All calls are server-side. Send `Authorization: Bearer $FAST_VIDEO_KEY` and `Content-Type: application/json`. - Creating a video is asynchronous: the POST returns 202 with an id at once. Then either poll GET every 3–5 seconds until status is `succeeded` or `failed`, or pass `webhook_url` and wait for our POST. - Download `video_url` into the user's own storage: files are deleted after 7 days. - Handle errors by `error.code` (see Errors). Do not retry 4xx errors unchanged; retry 429 and 5xx with backoff. - A clip uses EITHER start/end frames (`image`, `end_image`) OR references/audio (`references`, `audio`). Never both in one clip. ## Authentication Header: `Authorization: Bearer fv_…` (or `X-API-Key: fv_…`). Keys start with `fv_`. Get one by email at https://fast.video (comes with $1 of credit). Rotate: POST /v1/account/rotate-key (old keys stop at once). ## POST /v1/videos — one clip Body (JSON): - `prompt` string ≤ 2400 chars. What happens. Required unless `image` is given; required with `references`/`audio`. - `image` string. Start frame: https URL, `data:` URI, or upload id `upl_…`. - `end_image` string. End frame; needs `image`. The clip moves from one picture to the other. - `references` array of ≤ 5 strings (URL, data: URI or upload id). Pictures to keep: character sheets, outfits, props, products. Name them in the prompt ("the woman from the reference"). Not with `image`/`end_image`. - `audio` string (URL, data: URI or upload id of a WAV/MP3/M4A ≤ 10 MB). A voice or music track the people in the clip speak or sing along to; cut to `duration`. Put the spoken words in the prompt too. Not with `image`/`end_image`. - `duration` integer 5–15 seconds. Default 5. - `aspect_ratio` one of `16:9`, `9:16`, `1:1`, `4:3`, `3:4`. Default: matches `image`, else 16:9. - `webhook_url` https URL to POST the finished video to. Response 202 (and GET /v1/videos/{id}): ```json {"id": "vid_3f9c1a7b2d4e6f80", "object": "video", "status": "queued", "prompt": "…", "mode": "text", "duration": 5, "aspect_ratio": "16:9", "cost_usd": 0.045, "video_url": null, "poster_url": null, "created_at": 1793600000, "completed_at": null, "render_seconds": null, "error": null} ``` `status`: queued → running → succeeded | failed. `mode`: text, image, first_last, references, audio, references_audio, chained. When `succeeded`: `video_url` (mp4, 1264×720 landscape / 720×1264 portrait, AAC sound) and `poster_url` (jpg). When `failed`: `error` = {code, message} and `cost_usd` = 0 (refunded). GET /v1/videos?limit=20 lists recent clips (newest first). Example — prompt only: ```bash curl https://fast.video/v1/videos \ -H "Authorization: Bearer $FAST_VIDEO_KEY" -H "Content-Type: application/json" \ -d '{"prompt": "A red fox trots through fresh snow at dawn, slow tracking shot", "duration": 5}' ``` Example — start and end frame: ```json {"image": "https://example.com/shoe-side.jpg", "end_image": "https://example.com/shoe-back.jpg", "prompt": "The camera orbits slowly around the sneaker", "duration": 5} ``` Example — references with dialogue: ```json {"references": ["upl_4c1d…"], "audio": "upl_77e0…", "duration": 5, "prompt": "The woman from the reference sits in a cafe and says: \"No way, you did not!\" Medium close-up"} ``` ## POST /v1/uploads — images and sound files Multipart form, field `file`. Images: JPEG, PNG or WebP, ≤ 10 MB, ≥ 128 px a side. Sound: WAV, MP3 or M4A, ≤ 10 MB. ```bash curl https://fast.video/v1/uploads -H "Authorization: Bearer $FAST_VIDEO_KEY" -F file=@photo.jpg # {"id": "upl_9a0b…", "object": "upload", "kind": "image", "expires_at": …} ``` Use the id anywhere an image, reference or audio is accepted. Uploads last 24 hours. ## POST /v1/batches — many clips in one call Body: `{"items": [, …], "webhook_url": "…"}`. Up to 20 items. Each item is charged and refunded on its own. GET /v1/batches/{id} returns `{id, object: "batch", status, counts, videos: [...], cost_usd}`. Final status: succeeded, partial (some failed, refunded) or failed. ## POST /v1/sequences — long clips (up to 60 s) Segments of 5–15 s are rendered and joined into one mp4. Three modes: - `chain`: each segment starts on the exact last frame of the one before (continuous motion). Optional start `image`. - `keyframes`: `images` (≥ 2, in order); segment i runs from images[i] to images[i+1]. N images make N−1 segments. - `references`: every segment is made from the same `references` (≤ 5 pictures), so the cast and props match; segments join with cuts. Each segment may have its own `audio`. Body fields: `mode`, `prompt` (used by segments without their own), `duration` (shortcut: total seconds, split into ~10 s segments; chain and references modes), `image` (chain), `images` (keyframes), `references` (references mode), `segments` [{`prompt`, `duration` 5–15, `audio` (references mode)}], `aspect_ratio`, `join` (`cut` default or `crossfade`), `webhook_url`. Examples: ```json {"mode": "chain", "image": "https://example.com/street.jpg", "duration": 30, "prompt": "Handheld walk down a neon street at night, rain on the lens"} ``` ```json {"mode": "keyframes", "images": ["https://…/night.jpg", "https://…/dawn.jpg", "https://…/day.jpg"], "prompt": "Time-lapse, the camera holds still while the light changes", "segments": [{"duration": 5}, {"duration": 5}]} ``` ```json {"mode": "references", "references": ["https://…/robot-sheet.jpg"], "segments": [ {"prompt": "The robot from the reference rolls across the workbench", "duration": 5}, {"prompt": "Close-up: the same robot holds up a tiny wrench", "duration": 5}]} ``` GET /v1/sequences/{id} returns `{id, object: "sequence", status, mode, join, duration, video_url, poster_url, segments: [...], cost_usd, error}`. If a segment fails, later chain segments are cancelled and refunded; finished segments stay available. Chain segments render one after another, so a 30 s clip takes a few minutes — prefer a webhook. ## Webhooks Add `webhook_url` to any create call. We POST `{"type": "video.succeeded" | "video.failed" | "batch." | "sequence.", "data": }` and retry up to 4 times. Verify the header `X-FastVideo-Signature: sha256=` = HMAC-SHA256 of the raw request body with your `webhook_secret` from GET /v1/account: ```python import hmac, hashlib def valid(raw_body: bytes, header: str, secret: str) -> bool: return hmac.compare_digest(header, "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()) ``` ## Account and pricing - GET /v1/account → `{balance_usd, price_per_second_usd, last_30_days: {seconds, spent_usd}, webhook_secret, topup_amounts_usd}` - GET /v1/pricing → `{price_per_second_usd, examples_usd: {"5s", "10s", "30s", "60s"}}` - Cost of a clip = duration × price, charged when it starts, refunded if it fails. Top up at https://fast.video/account. ## Errors Shape: `{"error": {"code": "…", "message": "…"}}`. - 400 `invalid_request`, `invalid_image`, `invalid_audio`, `missing_prompt`, `references_with_frames`, `too_many_references`, `invalid_mode`, `too_long`, …: fix the request; the message says what. - 401 `invalid_key`: missing or revoked key. - 402 `insufficient_credit`: top up. - 422 `content_rejected`: blocked by the content filter; not charged. - 429 `rate_limited` (120 requests/min) or `too_many_active` (20 clips queued or rendering per account): wait and retry. - 5xx `server_error`: retry with backoff. Render failures arrive as `status: "failed"` with `error.code` (`generation_failed`, `content_rejected`, `timeout`) and are refunded. ## Limits 720p, 24 fps, AAC sound · 5–15 s per clip · long clips ≤ 60 s · batches ≤ 20 items · ≤ 5 references per clip · files kept 7 days, uploads 24 hours. ## Content rules No sexual content involving minors; no real, identifiable people without their consent; no deceptive deepfakes; no infringing content (full policy: https://fast.video/acceptable-use). Output is AI-generated: label it where the law requires and keep the files' metadata. If the user's app has its own users, the same rules apply to them. ## Minimal client (Python) ```python import os, time, requests API = "https://fast.video" H = {"Authorization": f"Bearer {os.environ['FAST_VIDEO_KEY']}"} def make_video(**params) -> str: v = requests.post(f"{API}/v1/videos", headers=H, json=params, timeout=60).json() if "error" in v: raise RuntimeError(v["error"]) while v["status"] in ("queued", "running"): time.sleep(3) v = requests.get(f"{API}/v1/videos/{v['id']}", headers=H, timeout=30).json() if v["status"] != "succeeded": raise RuntimeError(v["error"]) return v["video_url"] ```