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.
Prerequisites
Section titled “Prerequisites”- A MirAPI API key available as
MIRAPI_API_KEY. - Python 3 with the
requestslibrary (pip install requests). - A video-capable model enabled on your key, and enough prepaid balance to cover the clip.
How it works
Section titled “How it works”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.
POST /v1/video/generations— submit the prompt and receive atask_id.GET /v1/video/generations/{task_id}— poll untilstatusiscompletedorfailed.- Download the MP4 from the
urlon 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/videos → GET /v1/videos/{task_id} → GET /v1/videos/{task_id}/content) and Kling/Jimeng compatibility routes; see the video generation guide for the full matrix.
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. |
image is for image-to-video; this tutorial sends text only. To control the first frame, see the image-to-video tutorial.
The script
Section titled “The script”import osimport timeimport requests
BASE = "https://api.mirapi.ai/v1"HEADERS = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}POLL_INTERVAL = 10DEADLINE_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")What each branch handles
Section titled “What each branch handles”- Timeout: after 10 minutes the script raises
TimeoutErrorinstead of polling forever. RaiseDEADLINE_SECONDSfor longer clips. - Failure: a
failedstatus exits immediately with the gateway’serrormessage. - HTTP errors:
raise_for_statussurfaces 401 (bad key), 403 (whitelist or balance — top up), and 429 (slow down and back off). - Download: the completed task’s
urlis a temporary link — download it right away.
Verify
Section titled “Verify”python text_to_video.pyfile output.mp4curl 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.
Troubleshooting
Section titled “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_progress— 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”- Video generation guide — the full route matrix, parameters, and billing
- Image-to-video tutorial — first-frame control and compatibility routes
- Models & pricing — find video-capable models and per-second pricing
- Billing & top-ups — balance, top-ups, and reconciliation