Video generation
Video generation is asynchronous: a clip takes longer than a chat reply, so instead of blocking, the API returns a task that you submit, poll, and download.
Choose a model
Section titled “Choose a model”Video models are listed in GET /v1/models alongside chat models. Filter for entries whose output_modalities includes video, and check their pricing — video is billed per second of output, not per token. The vendor/ prefix in the console is for browsing only; it is not part of the model id you send to the API.
The three steps
Section titled “The three steps”POST /v1/video/generations— submit the job and receive atask_id.GET /v1/video/generations/{task_id}— poll untilstatusiscompletedorfailed.- Download the MP4 from the
urlin the completed task.
import osimport timeimport requests
BASE = "https://api.mirapi.ai/v1"headers = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}
# Step 1: submitresp = requests.post( f"{BASE}/video/generations", headers=headers, json={ "model": "deepseek-chat", "prompt": "A golden retriever playing fetch on a sunny beach", "duration": 5, "width": 1280, "height": 720, "fps": 30, },)resp.raise_for_status()task_id = resp.json()["task_id"]
# Step 2: pollwhile True: task = requests.get(f"{BASE}/video/generations/{task_id}", headers=headers).json() if task["status"] in ("completed", "failed"): break time.sleep(10)
if task["status"] == "failed": raise SystemExit(f"Video failed: {task.get('error')}")
# Step 3: downloadvideo = requests.get(task["url"])open("output.mp4", "wb").write(video.content)# Step 1curl -X POST https://api.mirapi.ai/v1/video/generations \ -H "Authorization: Bearer $MIRAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "deepseek-chat", "prompt": "A city skyline at dusk", "duration": 5}'
# Step 2 — repeat until "completed"curl https://api.mirapi.ai/v1/video/generations/abcd1234efgh \ -H "Authorization: Bearer $MIRAPI_API_KEY"
# Step 3 — download the URL from the completed taskcurl -o output.mp4 "https://…"The completed task’s url is a temporary link — download it right away, before it expires.
Request parameters
Section titled “Request parameters”The unified route takes a JSON body with these fields:
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | yes | Video-capable model id from the catalogue. |
prompt |
string | yes | Text description of the clip. |
image |
string | no | First-frame image for image-to-video: HTTP(S) URL or base64 data URI. |
duration |
number | no | Target length in seconds. |
width |
integer | no | Output width in pixels. |
height |
integer | no | Output height in pixels. |
fps |
integer | no | Frame rate. |
seed |
integer | no | Seed for reproducible output (not guaranteed by every model). |
n |
integer | no | Number of videos to generate. |
response_format |
string | no | Response format (e.g. url). |
user |
string | no | End-user identifier for abuse tracking. |
metadata |
object | no | Model-specific extras (negative_prompt, style, quality_level, …) where the model accepts them. |
Task status
Section titled “Task status”status is one of queued, in_progress, completed, failed. A completed task includes the video url, format (e.g. mp4), and a metadata object with the actual duration, width, height, and fps; a failed task includes an error with a code and message.
{ "task_id": "abcd1234efgh", "status": "completed", "url": "https://…", "format": "mp4", "metadata": { "duration": 5, "width": 1280, "height": 720 }}Poll every 10–30 seconds — a job can take minutes, depending on the model and resolution.
Image-to-video
Section titled “Image-to-video”Pass an optional image field (URL or base64 data URI) to control the first frame. The model keeps the composition, so describe the motion in prompt rather than re-describing the scene:
resp = requests.post( f"{BASE}/video/generations", headers=headers, json={ "model": "deepseek-chat", "prompt": "The photo comes to life and the person waves", "image": "https://example.com/first-frame.png", "duration": 5, },)Endpoint routes
Section titled “Endpoint routes”MirAPI exposes four route families for video. They all follow the same submit → poll → download flow, but differ in request format and response shape.
OpenAI / Sora format
Section titled “OpenAI / Sora format”POST /v1/videos—multipart/form-datawithmodel,prompt,seconds(a string), and an optionalinput_referenceimage file. Returns anid(nottask_id).GET /v1/videos/{task_id}— task status, includingprogressandcreated_at.GET /v1/videos/{task_id}/content— the finished file, returned asvideo/mp4.
Unified format
Section titled “Unified format”POST /v1/video/generations— JSON body (the request parameters above). Returnstask_idandstatus.GET /v1/video/generations/{task_id}— status; a completed task carriesurl,format, andmetadata.
Kling format
Section titled “Kling format”POST /kling/v1/videos/text2videoandPOST /kling/v1/videos/image2video— the same JSON body as the unified route; the image variant takesimage.- Poll via
GET /kling/v1/videos/text2video/{task_id}andGET /kling/v1/videos/image2video/{task_id}.
Jimeng format
Section titled “Jimeng format”POST /jimeng/— the native Jimeng API.ActionandVersionare query parameters:ActionisCVSync2AsyncSubmitTask(submit) orCVSync2AsyncGetResult(fetch result), andVersionis the API version (e.g.2022-08-31). The JSON body carriesreq_key,prompt, and an optionalbinary_data_base64image array.
Billing
Section titled “Billing”Video is billed per second of generated output, settled when the job completes. Failed jobs produce no output and are not charged.
- Balance exhausted → the request fails with
403; top up in the console and the same key recovers immediately. - Reconcile spend in the console billing log,
GET /api/usage/token(account-level summary), orGET /api/log/token(per-request detail).
Errors and troubleshooting
Section titled “Errors and troubleshooting”| Status | Meaning | What to do |
|---|---|---|
400 |
Invalid parameters | Check the model id, and that duration/width/height are numeric. |
401 |
Missing or invalid key | Verify Authorization: Bearer. |
403 |
Key valid but rejected | Top up (empty balance), or check the model whitelist, quota, and IP allowlist. |
404 |
Task or content not found | The task does not exist, or the video is not completed yet. |
429 |
Rate limited | Back off exponentially (1s → 2s → 4s, cap ~30s); there is no Retry-After header. |
500 |
Gateway or upstream failure | Retry idempotent requests safely. |
Every error message ends with a request ID — include it in support tickets.
- Task stays
queuedorin_progressfor a while — normal; jobs can take minutes. Keep polling at a steady interval. - Generation
failed— read theerrorfield, confirm the model has video output, and check that any reference image is reachable and in a supported format. 404on the content endpoint — the task is notcompletedyet; poll the status endpoint first.
Related links
Section titled “Related links”- Text-to-video tutorial — full submit/poll/download script with timeout and failure branches
- Image-to-video tutorial — first-frame control and compatibility routes
- Models & pricing — find video-capable models and per-second pricing
- Billing & top-ups — how balance, top-ups, and reconciliation work