LLM & Media APIVideo generation

Video generation

Text-to-video, image-to-video, first/last-frame and reference-to-video across Veo 3.1, Kling, Seedance, Sora, Wan and Runway — one async job API, charged only on delivery.

Every video model in the catalog runs on one async surface: POST /v1/llm/videos/jobs returns a job_id in under a second, and you poll GET /v1/llm/videos/jobs/{job_id} until the status is terminal. There is no synchronous video endpoint — nothing to migrate, no connection to lose.

You are charged only when a poll returns a video.url. A failed or timed-out job costs nothing.

Quick start

The catalog covers text-to-video (t2v — prompt only), image-to-video (i2v — pass image_url as the start frame), first/last-frame interpolation, reference-to-video, and video-to-video motion control (v2v — pass video_url plus image_url for the conditioning frame). Avatar SKUs take an audio reference URL in the request body.

import time, requests

BASE = "https://api.gpuniq.com/v1/llm"
HEADERS = {"X-API-Key": "gpuniq_your_key"}

# 1. Kickoff
start = requests.post(
    f"{BASE}/videos/jobs",
    headers=HEADERS,
    json={
        "model": "kling-2-6",
        "prompt": "A small black cat slowly turns toward the camera at golden hour",
        "duration": 5,
        "audio": False,          # opt-in, doubles price on Kling 2.6 / 3.0
        "resolution": "1080p",   # 720p | 1080p | 4k (where supported)
    },
).json()
job_id = start["data"]["job_id"]
print(f"job: {job_id}, est cost: ${start['data']['estimated_cost_usd']}")

# 2. Poll — video models deliver in 30-90s; budget 5 minutes for the slowest variants
deadline = time.time() + 300
while time.time() < deadline:
    time.sleep(3)
    r = requests.get(f"{BASE}/videos/jobs/{job_id}", headers=HEADERS).json()
    d = r["data"]
    if d["status"] == "completed":
        print(f"video: {d['video']['url']}")
        print(f"cost: ${d['cost_usd']}, balance: ${d['balance_usd']}")
        break
    if d["status"] == "failed":
        print("failed:", d.get("error"))
        break

Request body

FieldTypeRequiredNotes
modelstringyesSlug from the catalog.
promptstringyesUp to 10 000 characters.
durationintnoSeconds. Every model has its own legal set — see the duration matrix below. An out-of-range value is a 400, not a silent clamp.
aspect_ratiostringno16:9 (default), 9:16, 1:1 where supported. The Seedance 2 line (incl. seedance-2-5) additionally accepts auto, 21:9, 4:3, 3:4.
image_urlstringnohttps URL or data URI — enables image-to-video (start frame).
last_frame_urlstringnohttps URL or data URI — end frame for first/last-frame interpolation. Supported on Kling 2.1-Pro / 2.6 / 3.0 / O3, Veo 3.1 (all tiers) and the whole Seedance 2 line (seedance-2, seedance-2-5, -fast, -mini). Requires image_url. The first image is always the FIRST frame, the second the LAST — order matters.
video_urlstringnohttps URL or data: URI — reference clip for motion-control v2v variants and Seedance 2 line reference-to-video. Links the host serves as a non-video Content-Type are re-hosted automatically.
video_urlsarraynoReference clips for reference-to-video (Seedance 2 line) — https URLs or data: URIs, up to 3 on seedance-2 / -fast / -mini and up to 10 on seedance-2-5. A data: URI, a share link that serves the file as application/octet-stream, or a link that redirects (shorteners, download endpoints) is re-hosted as a direct video/mp4 URL before dispatch — some upstreams refuse each of those shapes outright. Single-reference models use the first entry.
reference_image_urlsarraynoSeedance 2 line, Kling O3 and Veo 3.1 fast/lite — https URLs / data URIs of subject or style reference images (JPG/PNG/WebP). Up to 30 on seedance-2-5, 9 on the rest of the Seedance 2 line, 4 on Kling O3, 3 on Veo 3.1 fast/lite. Switches the job to reference-to-video mode.
audio_urlstringnoSeedance 2 line only — reference audio track (MP3/WAV). Requires at least one image or video reference in the same request.
audio_urlsarraynoSeedance 2 line only — up to 10 tracks ≤30 s combined on seedance-2-5, up to 3 tracks ≤15 s combined elsewhere; merged with audio_url.
resolutionstringnoSeedance 2: 480p / 720p (default) / 1080p / 4k, priced per second per resolution. seedance-2-5 / -fast / -mini: 480p / 720p (default) only. Kling 3.0 / O3: 720p / 1080p (default) / 4k. Kling 2.6 / 3.0 Motion Control: 720p / 1080p (default). Veo 3.1: 720p / 1080p / 4k. Kling 2.1 / 2.5 Turbo Pro: 1080p only. Default is 1080p for every Kling/Veo SKU when the field is omitted — the Seedance 2 line is the exception at 720p.
audioboolnoDefault false. Kling 2.6 / 3.0 double the price when true; on the Seedance 2 line it generates the clip's audio track (no surcharge).
modestringnostandard / pro (default) / master for Kling 2.1; turbo for 2.5 Turbo Pro.
character_orientationstringnoKling 2.6 / 3.0 Motion Control only. video (default) — the output follows the reference clip's framing, clip up to 30s; image — the output follows the character image, clip up to 10s. Other models ignore it.
seedintnoseedance-2 only — integer in [-1, 4294967295]; omit or pass -1 for a random seed. Reusing a seed with an otherwise identical request pulls the render toward the earlier one; it does not reproduce it frame for frame. Accepted but inert on seedance-2-5 / -fast / -mini; other models ignore the field.
output_formatstringnoContainer of the delivered file: mp4 (default, H.264), mov or webm. Not every route encodes every container — mov and webm are served on seedance-2-5, seedance-2 / -fast / -mini, kling-3-0, kling-o3-video, kling-2-6 and kling-2-5-turbo-pro; seedance-2-5 additionally serves mov on its second route. Every other model is MP4-only. A container the model's routes cannot encode is a 400 before the job starts — the field is never dropped silently, because a clip in the wrong container is a clip you would pay for twice. The kickoff receipt echoes it in inputs.output_format.
output_qualityintnoEncoder quality of the delivered file, 2099. The upstream default is 95, already the top of the visually lossless range — lower values shrink the file, higher ones cannot add detail the model did not render. Honoured on the same models as mov / webm above (the Seedance 2 line and Kling 3.0 / O3 / 2.6 / 2.5 Turbo Pro); a 400 elsewhere. No route exposes a bitrate or a codec: bitrate, bitrate_mode and codec are refused with a hint rather than ignored. If a clip still looks pixelated in the downloaded file at 100 % on a paused frame, the limit is the render itself — raise resolution (1080p / 4k where the model has it), not the encode.

Vendor spellings are accepted, unknown ones are refused. The media fields also answer to the names the model vendors use — reference_video_urls, reference_videos, reference_audio_urls, first_frame_url, end_frame_url, source_video_url, duration_seconds, and the camelCase of any field above. A field name we don't recognise that looks like an attachment is a 400 naming it: an input that silently fails to attach costs you a render, so we refuse the request instead. Each kickoff also returns data.inputs — the mode and the count of references that reached the model.

Duration by model

duration is not a free integer. Each family accepts its own set and anything outside it comes back as a 400 naming the legal values — we do not round to the nearest supported length, because silently rendering (and billing) 8 seconds when you asked for 5 is worse than a refusal.

ModelLegal durationDefault
veo-3-1-lite / veo-3-1-fast / veo-3-1-quality4, 6 or 8 — nothing else8
veo-3-1-fast / -lite with reference_image_urls8 only8
kling-2-1, kling-2-5-turbo-pro, kling-2-65 or 105
kling-3-05 or 10 (multi-shot to 15)5
kling-o3-videoany integer 3–155
kling-2-6-motion-control, kling-3-0-motion-control5 or 105
kling-avatar-pro, kling-avatar-standardany integer 1–155
seedance-2, seedance-2-fast, seedance-2-miniany integer 4–155
seedance-2-5any integer 4–305
seedance-1-0-pro, seedance-1-5-pro55
sora-2-video, sora-2-pro-video1010
sora-2-official, sora-2-pro-official88

Veo does not take duration: 5. It is the single most common 400 on this surface — 5 is the default nearly everywhere else in the catalog, so it gets copied across from a Kling example. Veo's native set is 4 / 6 / 8; ask for 5 and you get invalid_request: Veo 3.1 supports durations of 4, 6 or 8 seconds (got 5).

Reference media: what format to send

Every media field takes a URL string, never an uploaded file part — there is no multipart endpoint. Two forms are accepted:

FormLooks likeWhere it works
Public HTTPS URLhttps://cdn.example.com/ref.jpgevery media field
Data URI (base64 inline)data:image/jpeg;base64,/9j/4AAQ…every media field

Upstream renderers only accept URLs, so a data: URI on a video or audio field is materialised into a real link on our side before dispatch. You do not have to host anything yourself to use a reference clip.

Hosted links are re-hosted when the host misdescribes them. Upstream validators judge a reference clip by the Content-Type the host declares, not by the bytes — so a perfectly good MP4 served as application/octet-stream used to be refused on sight. GPUniq now probes that header and, when it is not a video/* type, fetches the file and re-serves it correctly before dispatch.

Google Drive / Dropbox / OneDrive share links now work. Drive serves files as application/octet-stream with X-Content-Type-Options: nosniff and the original filename (IMG_3752.MOV), which upstream validators reject — this was the single most common cause of "my motion-control job will not start". Such links are re-hosted automatically as of 2026-08-15.

The link must still be directly downloadable without signing in — set the share to "anyone with the link", and use the uc?export=download&id=… form rather than a /view page. A link that returns an HTML sign-in page has no file behind it for us to fetch.

Serving a reference from a bucket with the right Content-Type is still the fastest path: it skips the extra fetch entirely. What the upstream fetch requires, in the order things usually go wrong:

  1. A direct link to the file, not to a viewer page. The response must be the bytes themselves.
  2. No authentication, no redirect chain to a login page. The fetcher is anonymous and has no cookies.
  3. A media Content-Typevideo/mp4, image/jpeg, audio/mpeg. Handled for you when it is wrong, at the cost of one extra round trip.
  4. A file extension that matches the content where possible — some upstreams sniff the URL path before they sniff the body.

Size and format limits:

FieldFormatsLimits
image_url, last_frame_urlJPG, PNG, WebPone image each
reference_image_urlsJPG, PNG, WebP≤30 seedance-2-5 · ≤9 rest of the Seedance 2 line · ≤4 Kling O3 · ≤3 Veo 3.1 fast/lite
video_url, video_urlsany common container (MP4, WebM, MKV, MOV)≤10 clips 2–30 s combined on seedance-2-5; ≤3 clips 2–15 s combined elsewhere; ≤50 MB total
audio_url, audio_urlsMP3, WAV≤10 tracks ≤30 s combined on seedance-2-5; ≤3 tracks ≤15 s combined elsewhere

Reference video resolution and container are unrestricted: 1080p/4K and non-MP4 sources are transcoded to ≤720p MP4 on our side before dispatch, so send source footage as-is. Keep in mind that a data URI inflates by ~33% over the raw bytes and counts against the request body limit — for anything above a few MB, host it and send a URL.

Which model takes which reference input

Sending a field the chosen model has no input for is a 400 at submit, not a silent drop. That is deliberate: previously a request carrying reference_image_urls could render on one attempt and be refused on the next, depending on which internal route served it. Capability is now resolved before dispatch, so the same request gets the same answer every time.

Modelimage_urllast_frame_urlreference_image_urlsvideo_urlaudio_url
seedance-2-5✅ ≤30✅ ≤10✅ ≤10
seedance-2✅ ≤9✅ ≤3✅ ≤3
seedance-2-fast, seedance-2-mini✅ ≤9✅ ≤3✅ ≤3
kling-o3-video✅ ≤4
veo-3-1-fast / veo-3-1-lite✅ ≤3 (8 s only)
veo-3-1-quality
kling-2-6-motion-control / kling-3-0-motion-controlrequiredrequired
kling-2-1, kling-2-5-turbo-pro, kling-2-6, kling-3-0✅ (2.1/2.6/3.0)
seedance-1-0-pro, seedance-1-5-pro

For subject or style reference on a model with no reference_image_urls column, use image_url as the start frame — or move to the Seedance 2 line (seedance-2 / seedance-2-5), which has the widest reference surface in the catalog.

Motion control is the strictest shape here: kling-2-6-motion-control and kling-3-0-motion-control need both image_url (the character / subject frame) and video_url (the motion to transfer). Either one alone is a 400 naming the missing field.

Response

The kickoff returns immediately with the GPUniq job id, the resolved parameter snapshot, and the cost estimate. Internal routing is opaque — the same job_id is valid across fallbacks, and the user-facing price stays stable.

// POST /v1/llm/videos/jobs
{
  "job_id": "vid_e93e98c7ca5e4982876b",
  "status": "pending",
  "model": "kling-2-6",
  "estimated_cost_usd": 0.315,
  "config": { "resolution": "1080p", "audio": false, "duration": 5, "task": "t2v", "mode": null }
}

// GET /v1/llm/videos/jobs/{job_id} — completed
{
  "job_id": "vid_e93e98c7ca5e4982876b",
  "status": "completed",
  "model": "kling-2-6",
  "video": {
    "url": "https://cdn.example.com/.../output.mp4",
    "stored_url": "https://api.gpuniq.com/v1/llm/media/8xK2p….mp4",
    "stored_expires_in_days": 7
  },
  "cost_usd": 0.315,
  "balance_usd": 9.17825791,
  "config": { "resolution": "1080p", "audio": false, "duration": 5, "task": "t2v", "mode": null }
}

The polling endpoint transparently falls back across internal routes if the first attempt fails — your job_id and the user-facing price stay stable across fallbacks. Internal route identifiers are deliberately omitted from the public response; they live only in admin/operator logs.

Where delivered media lives

Every generated image and video is also mirrored to GPUniq storage, and the response tells you where:

FieldWhat it is
video.url / image.b64_jsonthe original delivery — live immediately, unchanged
video.stored_url / image.urla GPUniq-hosted copy at https://api.gpuniq.com/v1/llm/media/{token}
stored_expires_in_days / expires_in_dayshow long that copy is kept (7 days by default)

Why it matters: the upstream video.url points at the rendering provider's CDN, and its lifetime is set by them, not by us. If you store that link rather than the bytes, it can stop resolving well before you expect. stored_url is ours and lives exactly as long as the field says.

This is a delivery buffer, not an archive. Copies are deleted automatically when the retention window closes and are not recoverable afterwards. If you need media permanently, download it and keep it on your own storage — treat stored_url as the safety net that gets you from "the job finished" to "the bytes are in my bucket", not as the bucket.

Two practical notes. For images the copy is written before the response is sent, so url works the moment you receive it. For videos the copy runs in the background — a clip is tens of megabytes and we would rather not make the delivering poll wait on it — so stored_url becomes valid shortly after the response; url is live immediately either way. And if storage is briefly unavailable the stored_url / url fields are simply absent: the generation still succeeds and is still delivered, because a storage hiccup is never allowed to fail a render you paid for.

Models & pricing

Video models are billed per delivered video or per second of output — the Headline / video column is the cheapest default configuration, not a flat rate. The exact amount charged is always in the completion response's cost_usd.

FamilySlugHeadline / videoNotes
OpenAI Sora 2sora-2-video$0.600Sora 2, default 10s
OpenAI Sora 2 Prosora-2-pro-video$1.000Premium quality, 10s
Sora 2 Officialsora-2-official$0.4808s, official API
Sora 2 Pro Officialsora-2-pro-official$0.5608s 1080p, official API
Google Veo 3.1 Liteveo-3-1-litefrom $0.30720p / 1080p / 4K, 4–8s; i2v, first/last frame, reference-to-video
Google Veo 3.1 Fastveo-3-1-fastfrom $0.60720p / 1080p / 4K, 4–8s; i2v, first/last frame, reference-to-video
Google Veo 3.1 Qualityveo-3-1-qualityfrom $2.50Flagship; 720p / 1080p / 4K, 4–8s; i2v, first/last frame
Kling 2.1 Prokling-2-1$0.405Standard / Pro / Master tiers, 5s or 10s, i2v
Kling 2.5 Turbo Prokling-2-5-turbo-pro$0.3155s or 10s, t2v / i2v
Kling 2.6kling-2-6$0.315Optional audio, 5s or 10s, t2v / i2v
Kling 3.0kling-3-0$0.504720p / 1080p / 4K, audio, multi-shot to 15s
Kling O3 (Video)kling-o3-videofrom $0.076 / s720p / 1080p / 4K, native audio, 3–15s; t2v / i2v, first+last frame, reference images (≤4)
Kling 2.6 Motion Controlkling-2-6-motion-control$0.504720p / 1080p video-to-video
Kling 3.0 Motion Controlkling-3-0-motion-control$0.756720p / 1080p video-to-video
Kling AI Avatar Prokling-avatar-pro$1.0351080p lip-sync, up to 15s
Kling AI Avatar Standardkling-avatar-standard$0.506720p lip-sync, up to 15s
Hailuo 02hailuo-02$0.200768p, 6s default
Hailuo 2.3hailuo-2-3$0.350768p 6s
Seedance 1.0 Proseedance-1-0-pro$0.210ByteDance 720p, 5s, t2v + i2v (per video)
Seedance 1.5 Proseedance-1-5-pro$0.160ByteDance 720p, 5s, t2v + i2v (per video)
Seedance 2seedance-2$0.122–$1.40 / sByteDance 480p–4k, billed per second, rate depends on resolution (720p: $0.272/s, 5s ≈ $1.36)
Seedance 2.5seedance-2-5$0.198–$0.426 / sByteDance 480p / 720p only, 4–30s, billed per second (720p: $0.426/s, 5s ≈ $2.13); first/last frame, reference images + video + audio
Seedance 2 Fastseedance-2-fast$0.13–$0.27 / sFaster, cheaper Seedance 2 line — 480p / 720p, 4–15s, billed per second
Seedance 2 Miniseedance-2-mini$0.08–$0.17 / sSmallest Seedance 2 line — 480p / 720p, 4–15s, billed per second
Alibaba Wan 2.2 Fastwan-2-2-fast$0.120720p fast tier
Alibaba Wan 2.5wan-2-5$0.600720p 5s
Alibaba Wan 2.6wan-2-6$0.800720p 5s flagship
Wan Animatewan-animate$0.150720p animation
Happy Horsehappy-horse$0.160720p
Grok Imagine Videogrok-imagine-video$0.300xAI video, 6s
Grok Imagine Video 1.5grok-imagine-video-1-5-previewfrom $0.08 / sxAI, billed per second, rate by resolution
Runway Gen-4.5runway-gen-4-5$0.750Runway flagship 5s

Kling SKUs are billed at −10% off the official public price. The headline above is the cheapest default configuration (1080p / no audio / 5s / Pro tier). Audio, longer duration, 4K, and Master tier scale the price linearly off the underlying reference rate × 0.9 — the exact cost is returned in the cost_usd field of the completion response. A 10% margin floor against the upstream supplier guarantees we never bill below source cost, so on a provider fallback the price may rise by 1-3%.

Veo 3.1 (Google)

All three Veo tiers are billed per video, by resolution. Duration can be 4, 6 or 8 seconds (default 8) and does not change the price.

ResolutionLiteFastQuality
720p$0.30$0.60$2.50
1080p (default)$0.35$0.65$2.55
4k$1.50$1.80$3.70

Omitting resolution gives you 1080p, not 720p — the video job surface defaults to 1080p across the whole Kling/Veo family, so the cheapest row is opt-in. Pass "resolution": "720p" explicitly if that is what you are budgeting for. The kickoff response echoes the resolved value in config.resolution and prices it in estimated_cost_usd, so you can always check before the render runs.

  • First + last frameimage_url (first frame) + last_frame_url (last frame) on all three tiers; the clip interpolates between the two frames and the order is honoured.
  • Reference-to-video — up to 3 reference images via reference_image_urls on veo-3-1-fast / veo-3-1-lite (8-second clips only; not available on Quality). Mutually exclusive with last_frame_url.
  • aspect_ratio: 16:9 (default), 9:16, or auto (follows the input image geometry).
  • On a provider fallback the charge follows the serving upstream's rate — the exact amount is always in the completion cost_usd.

Kling O3

Kling O3 is billed per second, by resolution and audio flag, for clips of 3–15 seconds (default 5). Prompts are capped at 2500 characters.

ResolutionAudio offAudio on
720p$0.0756 / s$0.1008 / s
1080p (default)$0.1008 / s$0.1260 / s
4k$0.3780 / s$0.3780 / s
  • Text-to-video — prompt only; aspect_ratio is 16:9 (default), 9:16 or 1:1.
  • Image-to-videoimage_url (start frame), optionally with last_frame_url (end frame). The aspect ratio follows the frame.
  • Reference-to-video — up to 4 reference images via reference_image_urls, which define the subject/style. image_url / last_frame_url may ride along as start / end anchors.
  • No reference video. video_url / video_urls are rejected with a 400 on this model: the upstream accepts a clip at submit and then fails the render. For video-to-video use kling-2-6-motion-control / kling-3-0-motion-control.

Seedance (ByteDance)

Seedance is ByteDance's text-to-video / image-to-video family — fast, photoreal clips, well suited to product shots, social content, and image-to-video animation of a still frame. Six SKUs are in the catalog, and they do not all price the same way, so read this before budgeting a batch.

SlugModelResolutionDuration (default)ModesBilling
seedance-1-0-proSeedance 1.0 Pro720p5st2v, i2vFlat $0.210 / video
seedance-1-5-proSeedance 1.5 Pro720p5st2v, i2vFlat $0.160 / video
seedance-2Seedance 2480p / 720p / 1080p / 4k4–15s (5)t2v, i2v, first/last-frame, ref-to-video (image + video + audio refs)Per second, by resolution — see below
seedance-2-5Seedance 2.5480p / 720p only4–30s (5)t2v, i2v, first/last-frame, ref-to-video (image + video + audio refs)Per second, by resolution — see below
seedance-2-fastSeedance 2 Fast480p / 720p4–15s (5)t2v, i2v, first/last-frame, ref-to-videoPer second, by resolution
seedance-2-miniSeedance 2 Mini480p / 720p4–15s (5)t2v, i2v, first/last-frame, ref-to-videoPer second, by resolution

The whole Seedance 2 line is billed per second of generated video, not per clip — and the rate depends on the resolution you pick.

seedance-2:

Resolution$/second$/second with reference video(s)
480p$0.122$0.073
720p (default)$0.272$0.163
1080p$0.612$0.368
4k$1.40$0.84

seedance-2-5 (no 1080p / 4k tier upstream):

Resolution$/second$/second with reference video(s)
480p$0.198$0.119
720p (default)$0.426$0.255

seedance-2-fast / seedance-2-mini:

Resolutionfast $/sfast + ref videomini $/smini + ref video
480p$0.13$0.075$0.08$0.05
720p (default)$0.27$0.165$0.17$0.104

A 5-second 720p seedance-2 generation (the default) costs 5 × $0.272 = $1.36; the same clip on seedance-2-5 is 5 × $0.426 = $2.13. Reference-to-video jobs use the lower per-second rate but are billed for the output duration plus the reference-clip seconds (the upstream meters reference footage too). The clip length isn't knowable at submit, so the kickoff estimated_cost_usd assumes the worst case — the input cap, 15 s on seedance-2 / -fast / -mini and 30 s on seedance-2-5 — and the final cost_usd is recomputed from the measured clip length, so short references settle cheaper than the estimate. A 5s 720p seedance-2-5 ref-to-video job estimates (5 + 30) × $0.255 = $8.93 and bills (5 + your actual clip seconds) × $0.255. The two -pro SKUs, by contrast, are a flat per-video price regardless of duration. Always read the cost_usd field of the completion response for the exact amount charged.

Choosing between them:

  • seedance-1-5-pro — cheapest ($0.160/clip), newest of the "pro" tier. Best default for short 720p clips where you want a fixed, predictable price.
  • seedance-1-0-pro — the previous pro model ($0.210/clip); keep using it only if you've tuned prompts against it.
  • seedance-2 — the high-quality tier with the widest resolution menu (up to 4k). Priced per second, so it scales with clip length.
  • seedance-2-5 — the newest ByteDance line and the flagship of the family. Best motion coherence and the only SKU that renders up to 30 seconds in one job, but it renders 480p / 720p only and is the most expensive per second. Reach for it when quality and clip length matter more than cost.
  • seedance-2-fast / seedance-2-mini — the same Seedance 2 grammar at a fraction of the price (480p / 720p, 4–15s). Use them for drafts, batch previews and A/B prompt work, then re-render the keeper on seedance-2 or seedance-2-5.

Text-to-video (default) needs only a prompt. Image-to-video animates a still: pass image_url (an https URL or data: URI) as the start frame. Every Seedance SKU supports both.

import time, requests

BASE = "https://api.gpuniq.com/v1/llm"
HEADERS = {"X-API-Key": "gpuniq_your_key"}

# Image-to-video with the flagship, 6-second clip
start = requests.post(
    f"{BASE}/videos/jobs",
    headers=HEADERS,
    json={
        "model": "seedance-2",
        "prompt": "the product slowly rotates on a marble pedestal, soft studio light",
        "image_url": "https://example.com/product.jpg",  # start frame → image-to-video
        "duration": 6,          # seedance-2 bills per second → 6 × $0.272 = $1.632
    },
).json()["data"]
job_id = start["job_id"]
print("estimated:", start["estimated_cost_usd"])   # 1.632

deadline = time.time() + 300
while time.time() < deadline:
    time.sleep(3)
    d = requests.get(f"{BASE}/videos/jobs/{job_id}", headers=HEADERS).json()["data"]
    if d["status"] == "completed":
        print("video:", d["video"]["url"], "cost:", d["cost_usd"])
        break
    if d["status"] == "failed":
        print("failed:", d.get("error"))
        break

Seedance shares the standard video job API. The two -pro SKUs (seedance-1-0-pro, seedance-1-5-pro) do text-to-video and image-to-video only: duration and image_url are the fields that matter, aspect_ratio is honoured where the upstream supports it, and fields specific to other families (audio, mode, resolution tiers, video_url) are ignored.

The Seedance 2 line (seedance-2, seedance-2-5, seedance-2-fast, seedance-2-mini) accepts more of the request body. On top of t2v and i2v it supports:

  • First + last frame — pass image_url (start) together with last_frame_url (end); the clip interpolates between the two frames.
  • Reference-to-video — condition the render on reference clips (video_url / video_urls), reference images (reference_image_urls) and reference audio tracks (audio_url / audio_urls), in any combination. The counts are per SKU: seedance-2, -fast and -mini take 9 images, 3 clips and 3 audio tracks, seedance-2-5 takes 30 images, 10 clips and 10 audio tracks. Audio references need at least one image or video reference alongside them (the API returns a 400 otherwise). A single image_url sent together with any reference input is treated as a subject reference rather than a start frame. First/last-frame and reference-to-video are mutually exclusive — when any reference input is attached, last_frame_url is dropped.
  • Resolution choice — via resolution; the per-second price scales with it (see the pricing tables above). The menu is per SKU: seedance-2 renders 480p / 720p / 1080p / 4k, while seedance-2-5, -fast and -mini render 480p / 720p only and return a 400 for anything above.
  • Generated audio track — set audio: true to have the model generate sound for the clip. Default is false; we always send the flag explicitly, so an omitted field means a silent clip.

duration applies to every Seedance SKU. seedance-2, -fast and -mini accept 4–15 seconds; seedance-2-5 accepts 4–30. On the Seedance 2 line, aspect_ratio additionally accepts auto, 21:9, 4:3 and 3:4 — omit it in i2v mode to follow the input frame's geometry.

seedance-2 also accepts a seed. Be aware of what it actually buys: keeping the seed fixed while everything else stays identical makes two renders markedly more alike than two unseeded ones, but they will not be identical — the model is not bit-reproducible, so treat the seed as a similarity control rather than a repeat button. A seeded request is routed to the one upstream that implements the parameter; if that route is unavailable the clip is still rendered, without the seed. On seedance-2-5, -fast and -mini the field is accepted but has no effect — the upstreams serving those SKUs expose no seed parameter, so the render is always random.

Reference-file limits: videos 2–15s combined (up to 30s on seedance-2-5), ≤50MB total; images JPG/PNG/WebP; audio MP3/WAV, ≤15s combined (30s on seedance-2-5). Video resolution and container are unrestricted — 1080p/4K clips and non-MP4 formats (webm, MKV, …) are automatically converted to ≤720p MP4 on our side before dispatch, so send your source footage as-is.

There is no elements parameter. Seedance 2's multi-element / multimodal reference capability is expressed through the fields above — image_url / last_frame_url for frames, video_url / video_urls for reference clips, reference_image_urls for subject/style images and audio_url / audio_urls for reference audio.

seedance-2-5 — exact request contract

The reference sheet for Seedance 2.5 specifically. Everything below is what the GPUniq API enforces today — a value outside these ranges is a 400 invalid_request at submit, not a silent clamp.

Endpoint. There is no model-specific route. Seedance 2.5 uses the standard async video-job API:

POST https://api.gpuniq.com/v1/llm/videos/jobs      → { job_id, estimated_cost_usd, ... }
GET  https://api.gpuniq.com/v1/llm/videos/jobs/{job_id}   → poll every 3–5 s

Auth is X-API-Key: gpuniq_... on both calls (a JWT works too). The kickoff returns in under a second; the job id stays valid across internal provider fallbacks and lives in our job store for 60 minutes, after which a poll returns job_not_found (404). Renders usually land in a few minutes; a job that shows no upstream progress for 30 minutes is failed definitively rather than left polling.

Model slug: seedance-2-5 (exactly this string in the model field; it is also listed by GET /v1/llm/models/catalog).

Request fields

FieldTypeAccepted on seedance-2-5Default
modelstring"seedance-2-5" — required
promptstringrequired, 1–10 000 characters
durationintany integer 4–30 (seconds)5
resolutionstring480p or 720p only1080p / 4k are a 400 on this SKU720p
aspect_ratiostring16:9, 9:16, 1:1, auto, 21:9, 4:3, 3:416:9 (auto follows the input frames)
image_urlstringhttps URL or data: URI — start frame (i2v)
last_frame_urlstringhttps URL or data: URI — end frame; requires image_url
video_urlstringone reference clip (https URL or data: URI)
video_urlsarray of stringup to 10 reference clips; merged with video_url
reference_image_urlsarray of stringup to 30 subject/style images (JPG/PNG/WebP)
audio_urlstringone reference audio track (MP3/WAV)
audio_urlsarray of stringup to 10 reference audio tracks; merged with audio_url
audiobooltrue makes the model generate the clip's soundtrackfalse
seedintaccepted by the schema, has no effect on this SKU
mode, character_orientationignored (Kling-family fields)

Generation modes. Four, and the frame modes and reference modes are mutually exclusive:

  1. Text-to-videoprompt only.
  2. Image-to-videoprompt + image_url (start frame).
  3. First + last frameprompt + image_url + last_frame_url. Order matters: image_url is always the FIRST frame.
  4. Reference-to-videoprompt + any of reference_image_urls / video_url / video_urls / audio_url / audio_urls. Attaching any reference input switches the job to reference mode: image_url is then read as one more subject reference and last_frame_url is not applied. Audio references require at least one image or video reference alongside them, otherwise the API returns a 400.

Write the prompt as a shot, not as an instruction about the clip. Seedance reads the prompt to decide what kind of job it was handed. A prompt that tells the model what to do with the attached clip — "follow @Video 1 exactly", "extend this shot" — is classified as video editing, and an edit's length and aspect ratio follow the source, which makes the duration / aspect_ratio in your request invalid. The job then fails with a message saying so.

Measured 2026-09-14 on one clip and one request shape, varying only the prompt: the instructing version failed four times out of four, while "a calm wide shot, cinematic" on the same reference rendered. So describe the shot you want — the reference still drives the performance, motion and look — or, when you really do want to work on the attached clip, use video_task and omit duration.

Reference-media limits on this SKU

InputFormatsLimit
reference_image_urlsJPG, PNG, WebP≤ 30 images (image_url counts as one of them in reference mode)
video_url + video_urlsany common container (MP4, WebM, MKV, MOV)≤ 10 clips, 2–30 s combined, ≤ 50 MB total
audio_url + audio_urlsMP3, WAV≤ 10 tracks, ≤ 30 s combined
image_url, last_frame_urlJPG, PNG, WebPone image each

Where the advertised "50 references" go. The headline for Seedance 2.5 is a sum: 30 images + 10 video clips + 10 audio tracks, and all three are accepted here as of 2026-09-09. The counts are per SKU, not per family — the seedance-2, -fast and -mini lines take 9 / 3 / 3, which is their own model's ceiling, and an over-cap array on either line is a 400 naming the field rather than a silent trim upstream.

Field names, and how to tell an input arrived. The model's own passport spells the reference arrays reference_video_urls / reference_image_urls / reference_audio_urls, and the start frame first_frame_url. Those spellings are accepted here and folded onto the names in the table above (so are their camelCase forms, reference_videos, end_frame_url, source_video_url and duration_seconds). A field name we don't recognise that looks like an attachment — motion_clip, image_urls, a typo of a real one — is a 400 naming it, never a render that quietly ignored it.

Every kickoff answers the question directly in data.inputs:

"inputs": {
  "mode": "reference-to-video",
  "reference_videos": 1,
  "reference_images": 2,
  "reference_audio": 0,
  "start_frame": false,
  "end_frame": false,
  "notes": ["`image_url` ships as reference image #1 — address it as @Image1 in the prompt."]
}

"reference_videos": 0 on a job built around a reference clip means the clip never reached the model — check that on the first call of a new integration rather than after a batch of renders.

Audio generation. "audio": true turns on the model's native soundtrack generation for the rendered clip. It is independent of audio_url / audio_urls, which are input references (pacing / lip-sync anchors), not output settings. The flag is always sent explicitly upstream, so omitting it reliably yields a silent clip.

Editing / extension

Supported on this SKU via video_task. Attach the clip you want to work on as video_url (or video_urls), address it in the prompt as @Video 1, and set:

video_taskWhat it doesduration
editRewrites the source in place — relight, remove or replace an element, restyle. Always returns a clip the same length as its source (within ~0.4 s).must be omitted
extendRenders new footage continuing the source. Say the direction in the prompt (Extend @Video 1 forward / backward).seconds to add, 4–30; omit to let the model choose

Both modes inherit the source's aspect ratio, so aspect_ratio is ignored, and both are mutually exclusive with image_url / last_frame_url — there is no start frame to set when the input is a whole clip. Scope an edit with plain language (Keep the storefront, lighting and camera move exactly unchanged); an edit aims at continuity, not pixel-identical preservation of the untouched regions.

Only seedance-2-5. Every other Seedance SKU renders one clip from one request; video_task on them returns a 400 rather than quietly generating something new. Edit/extend jobs are also pinned to the one upstream that implements the mode, so they don't participate in the usual provider failover.

Cost. Billed exactly like reference-to-video: (source seconds + output seconds) × the reference rate for your resolution. A 10 s source edited at 480p bills 20 s; the same source extended by 4 s bills 14 s. Because an edit's output length isn't knowable at submit, the kickoff estimated_cost_usd assumes the source's measured length and the final cost_usd is recomputed from the upstream invoice.

The adjacent post-processing surface is Topaz video upscaling (POST /v1/llm/topaz/video/jobs, see Topaz).

Cost

Resolution$/second$/second in reference-to-video mode
480p$0.198$0.119
720p (default)$0.426$0.255

Billed per second of output, i.e. rate × duration. In reference-to-video mode the upstream meters (input + output) seconds, so the reference-clip length is added to the billed seconds, capped at 30 s. Because clip length isn't knowable at submit, the kickoff estimated_cost_usd assumes that 30 s cap and the final cost_usd is recomputed from the measured length — a ref job with short clips settles well under its estimate.

Worked examples:

  • 5 s, 720p, t2v → 5 × $0.426 = $2.13
  • 30 s, 720p, t2v → 30 × $0.426 = $12.78
  • 10 s, 480p, t2v → 10 × $0.198 = $1.98
  • 8 s, 720p, ref-to-video with a 6 s clip → estimated (8 + 30) × $0.255 = $9.69, billed (8 + 6) × $0.255 = $3.57

Example request

# 1. Kick off — 8-second 720p reference-to-video with generated audio
curl -X POST https://api.gpuniq.com/v1/llm/videos/jobs \
  -H "X-API-Key: gpuniq_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2-5",
    "prompt": "the model walks toward camera through neon-lit rain, slow dolly-in, cinematic",
    "duration": 8,
    "resolution": "720p",
    "aspect_ratio": "9:16",
    "reference_image_urls": [
      "https://example.com/subject-front.jpg",
      "https://example.com/subject-side.jpg"
    ],
    "video_urls": ["https://example.com/motion-ref.mp4"],
    "audio": true
  }'

# → {"exception":0,"data":{"job_id":"vid_e93e98c7ca5e4982876b",
#                          "status":"pending","estimated_cost_usd":9.69,
#                          "inputs":{"mode":"reference-to-video",
#                                    "reference_videos":1,"reference_images":2,
#                                    "reference_audio":0,...},...}}

# 2. Poll every 3–5 s until status is completed or failed
curl https://api.gpuniq.com/v1/llm/videos/jobs/vid_e93e98c7ca5e4982876b \
  -H "X-API-Key: gpuniq_your_key"

# → {"exception":0,"data":{"status":"completed",
#                          "video":{"url":"https://.../out.mp4"},
#                          "cost_usd":3.57,"balance_usd":41.02}}

The simplest possible call is just the slug and a prompt — everything else has a default:

curl -X POST https://api.gpuniq.com/v1/llm/videos/jobs \
  -H "X-API-Key: gpuniq_your_key" \
  -H "Content-Type: application/json" \
  -d '{"model":"seedance-2-5","prompt":"a paper boat drifting down a rain gutter"}'