Skip to content

Text-to-video

This tutorial builds a complete Python script that turns a text prompt into a downloaded output.mp4 using MirAPI’s asynchronous video API. It covers the full submit → poll → download loop, plus timeout, failure, and error handling.

  • A MirAPI API key available as MIRAPI_API_KEY.
  • Python 3 with the requests library (pip install requests).
  • A video-capable model enabled on your key, and enough prepaid balance to cover the clip.

Video generation is asynchronous: a clip takes longer than a chat reply, so the API returns a task that you submit, poll, and download instead of blocking.

  1. POST /v1/video/generations — submit the prompt and receive a task_id.
  2. GET /v1/video/generations/{task_id} — poll until status is completed or failed.
  3. Download the MP4 from the url on the completed task.

status is one of queued, in_progress, completed, failed. Poll every 10 seconds or so — a job can take minutes, depending on the model and resolution. A completed task carries the video url, format (e.g. mp4), and a metadata object with the actual duration, width, height, and fps:

{
"task_id": "abcd1234efgh",
"status": "completed",
"url": "https://…",
"format": "mp4",
"metadata": { "duration": 5, "width": 1280, "height": 720 }
}

This tutorial uses the unified route. MirAPI also exposes an OpenAI/Sora route (POST /v1/videosGET /v1/videos/{task_id}GET /v1/videos/{task_id}/content) and Kling/Jimeng compatibility routes; see the video generation guide for the full matrix.

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.

image is for image-to-video; this tutorial sends text only. To control the first frame, see the image-to-video tutorial.

import os
import time
import requests
BASE = "https://api.mirapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}
POLL_INTERVAL = 10
DEADLINE_SECONDS = 600
def submit(prompt: str, duration: int) -> str:
resp = requests.post(
f"{BASE}/video/generations",
headers=HEADERS,
# Replace deepseek-chat with a video model id from GET /v1/models.
json={"model": "deepseek-chat", "prompt": prompt, "duration": duration},
timeout=60,
)
resp.raise_for_status()
return resp.json()["task_id"]
def poll(task_id: str) -> dict:
deadline = time.time() + DEADLINE_SECONDS
while time.time() < deadline:
resp = requests.get(f"{BASE}/video/generations/{task_id}", headers=HEADERS, timeout=30)
resp.raise_for_status()
task = resp.json()
print(f"status={task['status']}")
if task["status"] == "completed":
return task
if task["status"] == "failed":
raise SystemExit(f"Video failed: {task.get('error')}")
time.sleep(POLL_INTERVAL)
raise TimeoutError(f"Task {task_id} did not finish within {DEADLINE_SECONDS}s")
task_id = submit("A golden retriever playing fetch on a sunny beach", 5)
task = poll(task_id)
video = requests.get(task["url"], timeout=60)
video.raise_for_status()
with open("output.mp4", "wb") as f:
f.write(video.content)
print("Saved output.mp4")
  • Timeout: after 10 minutes the script raises TimeoutError instead of polling forever. Raise DEADLINE_SECONDS for longer clips.
  • Failure: a failed status exits immediately with the gateway’s error message.
  • HTTP errors: raise_for_status surfaces 401 (bad key), 403 (whitelist or balance — top up), and 429 (slow down and back off).
  • Download: the completed task’s url is a temporary link — download it right away.
Terminal window
python text_to_video.py
file output.mp4
curl https://api.mirapi.ai/api/log/token -H "Authorization: Bearer $MIRAPI_API_KEY"

Video is billed per second of generated output, settled when the job completes. Failed jobs produce no output and are not charged. A 5-second clip costs five times the model’s per-second rate; check the console billing log at https://console.mirapi.ai or GET /api/log/token for the exact figure.

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 queued or in_progress — normal; jobs can take minutes. Keep polling at a steady interval.
  • Generation failed — read the error field, confirm the model has video output, and check that any reference image is reachable and in a supported format.
  • 404 on the content endpoint — the task is not completed yet; poll the status endpoint first.