Upscale a Video

Use video upscaling when a finished MP4 needs a larger resolution for delivery on high-resolution screens or platforms. Choose a 1080p, 2k, or 4k target, and Mirako enlarges the video without cropping or stretching it.

Video upscaling is asynchronous. You receive a task ID first, then wait for the MP4 result or return later to retrieve it.

Before You Start

Prepare a video that meets these requirements:

Requirement Supported value
Container MP4
Video H.264, 8-bit SDR, progressive, constant frame rate, square pixels
Audio AAC or no audio
Maximum file size 1 GiB
Maximum duration 10 minutes
Frame limits Up to 60 fps and 18,000 total frames
Source dimensions Each side up to 4096 pixels and up to 2,100,000 pixels in total

Note: Every video upscale task has a minimum billable duration of 5 seconds. For example, a 2-second video is billed as 5 seconds. This minimum affects billing only; the output video keeps its original duration.

Choose a target larger than the source. Mirako preserves the source aspect ratio, fits it inside the selected target box, and limits enlargement to 4×.

Target Landscape box Portrait box
1080p 1920×1080 1080×1920
2k 2560×1440 1440×2560
4k 3840×2160 2160×3840

Note: The target is a bounding box, not a request to crop the video to an exact aspect ratio. For example, a 4:3 source remains 4:3.

If this is your first Mirako request, complete Authentication before continuing.

Quick Start with the CLI

Upscale a video to the 1080p target, wait for completion, and save the MP4:

sh
mirako video upscale \
  --video launch-clip.mp4 \
  --resolution 1080p \
  --output launch-clip-1080p.mp4

The CLI submits the task and polls until it can download the result. For a background workflow, submit without waiting and keep the printed task ID:

sh
mirako video upscale \
  --video launch-clip.mp4 \
  --resolution 2k \
  --no-wait

mirako video upscale status <task-id> --output launch-clip-2k.mp4

If the status command reports that work is still in progress, run it again later.

Option Required Description
--video Yes Path to the MP4 file.
--resolution, -r Yes Target: 1080p, 2k, or 4k.
--output, -o No Where to save the completed MP4.
--poll-interval, -p No Status interval in seconds. Defaults to 2.
--no-wait No Return after submission. Cannot be combined with --output.
--no-save, -n No Wait for completion and print the temporary result URL instead of downloading it.

Integrate with the REST API

Set your API key as the MIRAKO_API_KEY environment variable. The following example submits an MP4, polls every 10 seconds, and downloads the completed result. Store the task ID in a durable place if your process may restart.

python
import os
import shutil
import time
from pathlib import Path

import requests

API_KEY = os.environ["MIRAKO_API_KEY"]
BASE_URL = "https://mirako.co"
INPUT_PATH = Path("launch-clip.mp4")
OUTPUT_PATH = Path("launch-clip-1080p.mp4")
POLL_INTERVAL = 10
MAX_WAIT_SECONDS = 30 * 60

headers = {"Authorization": f"Bearer {API_KEY}"}

try:
    with INPUT_PATH.open("rb") as video_file:
        response = requests.post(
            f"{BASE_URL}/v1/video/upscale",
            headers=headers,
            params={"resolution": "1080p"},
            files={"file": (INPUT_PATH.name, video_file)},
            timeout=900,
        )
    response.raise_for_status()

    task = response.json()["data"]
    task_id = task["task_id"]
    print(f"Submitted task {task_id}")

    deadline = time.monotonic() + MAX_WAIT_SECONDS
    while task["status"] in {"IN_QUEUE", "IN_PROGRESS"}:
        if time.monotonic() >= deadline:
            raise TimeoutError(f"Task {task_id} did not finish in time")

        time.sleep(POLL_INTERVAL)
        status_response = requests.get(
            f"{BASE_URL}/v1/video/upscale/{task_id}",
            headers=headers,
            timeout=60,
        )
        status_response.raise_for_status()
        task = status_response.json()["data"]
        print(f"Status: {task['status']} ({task['stage']})")

    if task["status"] != "COMPLETED":
        error = task.get("error", {})
        raise RuntimeError(error.get("message", f"Task ended as {task['status']}"))

    output = task["result"]["output"]
    with requests.get(output["file_url"], stream=True, timeout=300) as download:
        download.raise_for_status()
        with OUTPUT_PATH.open("wb") as output_file:
            shutil.copyfileobj(download.raw, output_file)

    print(f"Saved {output['width']}×{output['height']} MP4 to {OUTPUT_PATH}")
    print(f"Result remains available until {output['readable_until']}")
except (requests.RequestException, RuntimeError, TimeoutError) as error:
    response = getattr(error, "response", None)
    detail = response.text if response is not None else str(error)
    raise SystemExit(f"Video upscale failed: {detail}") from error
Parameter Location Required Description
file Multipart form Yes MP4 video, up to 1 GiB.
resolution Query Yes 1080p, 2k, or 4k.
webhook_url Multipart form No Public HTTPS URL that receives the terminal task payload, up to 1000 bytes.
webhook_token Multipart form No Token sent as Authorization: Bearer <token>, up to 512 bytes. Requires webhook_url.
task_id Path For status checks ID returned by the submit request.

See the Upscale Video API reference for the complete request and response schema.

Use a Webhook Instead of Polling

For a serverless or event-driven application, include webhook_url in the multipart form. Mirako sends the terminal task payload once when processing completes. The URL must use public HTTPS, and redirects are not followed.

Use webhook_token to authenticate the callback, and still store the task ID so you can recover with GET /v1/video/upscale/{task_id} if delivery fails. See Handle Async Tasks for general polling and webhook guidance.

Download the Result Promptly

A completed task provides the temporary MP4 URL at data.result.output.file_url, plus dimensions, duration, frame rate, and readable_until.

The result remains available for 24 hours after it is created. A signed URL can expire sooner; poll the task endpoint again before readable_until to obtain a fresh URL. After that time, the API returns 410 RESULT_EXPIRED.

Going Next

Use Talking Avatar Videos or Avatar Motion Videos when you need to create a new avatar video before upscaling it.

Dive Deeper