Video v2.0 Cinematic

Turn a still render into architectural footage at 1080p. v2.0 Cinematic is the full-resolution tier of our v2.0 video engine — the same directed camera moves, scene control and synchronized ambient sound as v2.0 Flash, rendered at twice the size.

How it works: you send a start frame and, optionally, a prompt and a camera move. The engine composes a cinematography brief around them — the camera beats, the in-scene motion, a stability tail and a sound clause — and renders a 10 or 15 second clip. Generation is asynchronous: you get a request ID back immediately and poll for the result.

Cinematic vs Flash:

  • • Output size: Cinematic renders 1080p — the full-resolution refinement of the same source Flash renders at 768p
  • • Cost: 2 credits per second, against Flash's 1
  • • Everything else is identical: the same parameters, the same defaults, the same camera vocabulary, the same clip lengths, the same end-frame morph

Output size is the only difference, which is why there is no resolution parameter: the endpoint you pick is the resolution you get. A request written for one tier runs unchanged on the other.

Pricing

Video is billed per second, so a shorter clip costs less. Cinematic charges 2 credits per second of finished footage.

Clip lengthCinematic (this endpoint)Flash
10 seconds (default)20 credits10 credits
15 seconds30 credits15 credits

An end frame is free. Supplying end_image turns the run into a first-to-last-frame morph at no extra cost — the price reads only the tier and the length.

Endpoint

HTTP Method
POST https://api.mnmlai.dev/v1/video-v20-cinematic

Request

Send a POST request with multipart/form-data. A start frame is the only required field — everything else has a working default.

Parameters

ParameterTypeDefaultDescription
imageRequiredFile-The start frame (JPEG, PNG, WebP). Max 15MB, min 1KB. Send this or image_url.
image_urlString-A publicly reachable URL for the start frame, instead of uploading bytes. See the note under Frame Requirements about which checks run on a URL.
promptRecommendedStringemptyWhat should happen in the clip. Optional — with no prompt, the camera and motion settings carry the direction on their own. Max 2000 characters.
raw_promptString-Bypasses prompt composition entirely and sends your text to the model verbatim. prompt, camera_movement and motion are ignored when this is set.
durationInteger10Clip length in seconds. 10 or 15. 20 renders 15 seconds (and is billed as 15); any other value renders and is billed as 10.
camera_movementStringstaticHow the camera moves. A preset name, a raw move token, or two tokens comma-joined. See Camera Movement.
motionStringsubtleHow much happens in the scene, independent of the camera. One of auto, subtle, balanced, dynamic.
end_imageFile-An optional end frame. Supplying one renders a smooth morph from the start frame to this one instead of a free animation. Free — it does not change the price.
end_image_urlString-A publicly reachable URL for the end frame, instead of uploading bytes.
seedIntegerrandomReuse a seed to get a repeatable result from the same inputs. Returned in the response either way.

Parameters this engine does not have

  • • No negative_prompt. This engine takes no exclusion list. The stability direction that other engines put in a negative prompt is written into the composed prompt as positive assertions instead. Sending the field has no effect.
  • • No aspect_ratio. The clip takes its shape from the start frame. Crop the frame to change the shape of the video.
  • • No resolution. The endpoint is the resolution — 1080p here, 768p on Flash.
  • • No audio switch. Every clip is scored with quiet ambience matched to the scene. The composed prompt bounds it to room tone and outdoor air — no music, voices or narration.

Camera Movement

The simplest way to set the shot is a preset name:

PresetThe shot
autoNo camera instruction at all — your prompt decides. Falls back to static if you sent no prompt.
staticLocked-off camera; only the scene moves. The default.
walkthroughTracks right while panning left — moving through a space.
room-showcaseClockwise orbit around the subject, holding centre focus.
drone-viewRises vertically, then tilts down toward the ground plane.
pull-backPulls back to reveal the full scene, then tilts up.
hero-shotPushes in on the subject, then tilts up the elevation.
detail-focusSlow optical zoom in, tightening the framing.

For finer control, send raw move tokens instead. Up to two, comma-joined, and they run as beats in the order you write them (“dolly in, then tilt up”). A third token is dropped: stacked moves beyond two resolve unpredictably, so the engine will not sell you one.

Move tokens
dolly-in      dolly-out
truck-left    truck-right
pedestal-up   pedestal-down
pan-left      pan-right
tilt-up       tilt-down
roll-left     roll-right
zoom-in       zoom-out
orbit-left    orbit-right
static

Migrating from Video AI? That endpoint's vocabulary — pan_left, pan_right, tilt_up, tilt_down, zoom_in, zoom_out, static — is accepted here too and maps to the equivalent move, so an existing request keeps its shot.

How camera and motion interact

  • • At the default motion=subtle, choosing a camera move drops the scene-activity direction: the move carries the shot on its own. Asking for a dolly and for near-motionless scenery at the same time reads as two competing briefs, and the weaker one tends to win.
  • • At balanced or dynamic, both ship — that combination is a real request (a drone move over a street with traffic in it).
  • • With camera_movement=static, scene cues always ship. They are the only motion the clip has.
  • • auto on either field suppresses that field's direction so your prompt has an empty stage — but only if you actually sent a prompt.

Frame Requirements

RuleWhat happens
Aspect ratio between 1:2.5 and 2.5:1Enforced. A frame outside this range is refused with INVALID_FRAME_ASPECT_RATIO before any credits are spent, and the message names the ratio it measured. Panoramic sources are the usual cause — crop, or extend the canvas toward a squarer shape.
Large framesHandled for you. Frames are downscaled to a 2048px long edge before submit. Nothing above that can reach a 1080p clip, so there is no quality cost — an 8K upscale is accepted and fitted rather than refused.
Format and sizeJPEG, PNG or WebP; 1KB to 15MB per frame.
Morph framesWhen you send an end frame, both frames are checked. For the smoothest morph, give the two frames the same dimensions — the model interpolates between them pixel for pixel.

URLs are not measured locally. The aspect check runs on uploaded bytes. A frame sent as image_url or end_image_url is passed through and judged by the renderer, so an out-of-range URL frame fails later instead of being refused up front — your credits are returned automatically (see Refunds), but you get the answer a minute later rather than immediately. Upload the bytes if you want the fast, free check.

Response

202 — job accepted
{
  "status": "success",
  "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
  "seed": 482913,
  "prompt": "the building transitions from day to night",
  "parameters": {
    "tier": "cinematic",
    "duration": 15,
    "resolution": "1080P",
    "creditsPerSecond": 2,
    "cameraMovement": "static",
    "motion": "subtle",
    "shape": "first-to-last-frame",
    "audio": "ambient"
  },
  "cost": 30,
  "credits": 1210
}

cost is what this clip was charged and credits is your balance afterwards. The video itself is not ready yet — poll the status endpoint with the returned id.

Examples

cURL — a 1080p hero shot
curl -X POST https://api.mnmlai.dev/v1/video-v20-cinematic \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]" \
  -F "prompt=golden hour light rakes across the facade" \
  -F "camera_movement=hero-shot" \
  -F "motion=balanced" \
  -F "duration=15"
cURL — morph between two frames
curl -X POST https://api.mnmlai.dev/v1/video-v20-cinematic \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]" \
  -F "[email protected]" \
  -F "prompt=the building transitions from day to night" \
  -F "duration=15"
JavaScript — submit and poll
const form = new FormData();
form.append("image", fs.createReadStream("render.jpg"));
form.append("prompt", "camera glides through the entrance into the atrium");
form.append("camera_movement", "walkthrough");
form.append("motion", "balanced");
form.append("duration", "15");

const res = await fetch("https://api.mnmlai.dev/v1/video-v20-cinematic", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.MNML_API_KEY}` },
  body: form,
});

const { id, cost } = await res.json();
console.log(`Job ${id} started — ${cost} credits`);

// Poll until the clip is ready.
let result;
do {
  await new Promise((r) => setTimeout(r, 5000));
  result = await fetch(`https://api.mnmlai.dev/v1/status/${id}`, {
    headers: { Authorization: `Bearer ${process.env.MNML_API_KEY}` },
  }).then((r) => r.json());
} while (result.status === "processing");

console.log(result.message[0]); // the video URL
Python — draft on Flash, deliver on Cinematic
import os, time, requests

API = "https://api.mnmlai.dev/v1"
headers = {"Authorization": f"Bearer {os.environ['MNML_API_KEY']}"}

# Draft on Flash, then re-run the SAME request on Cinematic with the
# seed it returned once the shot is right.
def animate(endpoint, seed=None):
    with open("render.jpg", "rb") as f:
        data = {
            "prompt": "late afternoon light rakes across the courtyard",
            "camera_movement": "hero-shot",
            "motion": "subtle",
            "duration": "15",
        }
        if seed:
            data["seed"] = str(seed)
        return requests.post(
            f"{API}/{endpoint}", headers=headers, files={"image": f}, data=data
        ).json()

draft = animate("video-v20-flash")          # 15 credits
final = animate("video-v20-cinematic", draft["seed"])  # 30 credits

Checking Processing Status

Status Check Endpoint
GET https://api.mnmlai.dev/v1/status/{id}
Completed job
{
  "status": "success",
  "message": ["https://api.mnmlai.dev/api/v1/images/files/clip.mp4"],
  "seed": 482913
}

Processing time: video takes substantially longer than an image render, and 1080p takes longer than 768p — expect several minutes for a 15 second Cinematic clip. Poll every 5–10 seconds until the status is "success" or "failed". The returned URL serves an MP4 and supports range requests, so it plays directly in a browser or video element.

Refunds

A clip you cannot collect is refunded automatically. If the render fails, or completes without producing a video, the exact number of credits this clip was charged is returned to your balance — once, even if the failure is reported more than once. You do not need to ask for it, and there is nothing to reconcile: a 15 second Cinematic clip refunds 30, a 10 second Flash clip refunds 10.

Best Practices

Choosing a tier

  • Use Cinematic for final delivery, full-screen playback and pitch material — anywhere the extra resolution will actually be seen.
  • Use Flash for iteration, client previews and social. Half the price, and the composition is identical.
  • Draft on Flash with a fixed seed, then re-run the same request here once you like the shot — see the Python example above.

Frame guidelines

  • Start from a clean, sharp render — 1080p video amplifies artefacts in the source frame more than 768p does.
  • Keep the aspect between 1:2.5 and 2.5:1. Crop panoramas before sending them.
  • Upload bytes rather than a URL when you can: the aspect check then runs instantly and for free.
  • For a morph, match the two frames' dimensions and keep the same subject in roughly the same place.

Prompt tips

  • Describe visible motion, not adjectives: “clouds drift behind the tower, pedestrians cross the plaza” beats “dynamic and cinematic”.
  • Let one thing direct the camera. If you are describing the move in words, set camera_movement=auto so the preset does not compete with your sentence.
  • Do not ask for a shot the source cannot support — an orbit needs a subject with sides, not a flat elevation.
  • Reach for raw_prompt only when you want full control; you lose the stability and sound direction the composed prompt adds.

Error Handling

CodeStatusMeaning
MISSING_IMAGE400No start frame. Send image or image_url.
INVALID_FRAME_ASPECT_RATIO400A frame is outside 1:2.5–2.5:1. The message names the measured ratio; details.frame says which frame.
INVALID_IMAGE_TYPE400Not a JPEG, PNG or WebP.
IMAGE_TOO_LARGE / IMAGE_TOO_SMALL400A frame is outside 1KB–15MB.
INVALID_MOTION400motion is not one of auto, subtle, balanced, dynamic.
PROMPT_TOO_LONG400Over 2000 characters.
NO_CREDITS400Balance below this clip's price. details.required gives the price and details.duration the length it was quoted for.
INVALID_REQUEST_BODY400The body was not multipart/form-data.
USER_NOT_FOUND400The API key does not resolve to an account.
INTERNAL_SERVER_ERROR500Something failed on our side. Nothing is charged.

Every 400 above is returned before any credits are spent. See Error Handling for the shared error envelope.