Docs
Open console
Create

Create a render

Render an exterior, interior, masterplan, landscape, plan or product from an image, or from a prompt alone.

v2 previewPOST/v2/renders

Returns 202 straight away with the job and what it charged. Poll GET /v2/jobs/{id} for the result; a job that fails is refunded. The price is the engine’s: see Pricing.

Anything accepted but not applied, such as a setting that does not fit the engine, comes back in notes rather than being dropped silently. A field the API does not know is refused before anything is charged.

Sending the image

The image goes in the same call, with no upload step. Every create call takes it the same three ways, and so does each reference, an edit’s mask and a video’s end_frame:

  • A file on disk: send the call as multipart form data, with the file as the image part and every other field as a text part. Repeat references for several references.
  • A link: "image": "https://…" in JSON. mnml fetches it.
  • Bytes in your code: "image": "data:image/jpeg;base64,…" in JSON, or bare base64.
From a file
curl -X POST https://api.mnml.ai/v2/renders \  -H "Authorization: Bearer $MNML_API_KEY" \  -F [email protected] \  -F prompt="Timber facade, late afternoon, olive trees" \  -F [email protected]

In a form, numbers and true / false are written as text, and settings or a region as JSON. Images are JPEG, PNG or WebP, up to 15 MB each. To work on an image mnml already made for you, pass its job_id instead: nothing is downloaded or sent again.

Getting the result in the same call

Add ?wait=90 and the answer waits up to that many seconds (90 at most) while the render runs. If it finishes in time, the answer is a 200 and jobs holds every job with its outputs. If it is still running, the answer is the usual 202 with jobs as they stand. Then follow the job as a stream: one connection that stays open until the render is done, however long it takes. How long a render takes depends on the engine: compare the engines on the models page.

One call
curl -X POST "https://api.mnml.ai/v2/renders?wait=90" \  -H "Authorization: Bearer $MNML_API_KEY" \  -F [email protected] \  -F prompt="Timber facade, late afternoon, olive trees" # 200 → data.jobs[0].outputs[0].url is the render

For a slower engine, a video, or a script that should simply block until the result is ready, skip wait: start the job, then open its stream. It sends the job each time it changes and ends with done and the finished job.

Start, then follow
JOB_ID=$(curl -s -X POST "https://api.mnml.ai/v2/renders" \  -H "Authorization: Bearer $MNML_API_KEY" \  -F [email protected] \  -F prompt="Timber facade, late afternoon, olive trees" | jq -r .data.id) curl -N "https://api.mnml.ai/v2/jobs/$JOB_ID/events" -H "Authorization: Bearer $MNML_API_KEY" # event: done → data.outputs[0].url is the render

Headers

AuthorizationstringRequired
Bearer mk_live_…: your API key. See Authentication.
Content-TypestringRequired
application/json, or multipart/form-data to send a file.
Idempotency-Keystring
Your own key, 1 to 255 visible ASCII characters. A retry with the same key and request within 24 hours returns the first answer instead of running again.

Query parameters

waitinteger
Hold the answer up to this many seconds (1 to 90) while the render runs, and answer with the finished jobs in jobs. Still running when the time is up: a 202; read the job with GET /v2/jobs/{id}?wait=90 until it settles.

Body

enginestringDefault v4.6-ultra
An engine id or name from GET /v2/engines.
modestringDefault exterior
What the image shows. Without an image it defaults to text-to-render, which renders from the prompt alone.exteriorinteriormasterplanplanlandscapeproducttext-to-render
promptstringRequired
What to render: materials, light, season, context.
imagestring
The JPEG, PNG or WebP to work on, up to 15 MB: a public https:// link we fetch, a data:image/…;base64, URI, or bare base64. With multipart form data, attach the file itself (-F [email protected]). Send one of image or job_id.
job_idstring
A finished job of yours: work on its output without downloading it first.
referencesstring | object[]
Images whose style, materials, mood or geometry to follow, up to 15. Each is just the image (a link or base64, or with multipart a references file part, repeated), or an object, { image | job_id, mode }, that also sets its mode. How many an engine takes is capabilities.max_references in GET /v2/engines.
Fields of each item (3)
imagestring
The reference image: a public https:// link we fetch, a data:image/…;base64, URI, or bare base64. With multipart form data, attach the file itself. Send one of image or job_id.
job_idstring
The reference image, as a finished job of yours.
modestringDefault style
What to take from it: its style, a material, the atmosphere, the color or the geometry. auto lets the engine decide.autostylematerialatmospherecolorgeometry
settingsobject
Studio’s settings for this engine and mode, as name → value. A setting left out takes the value Studio opens on. A setting that does not apply is listed in notes, not applied.
aspect_ratiostringDefault auto
The frame. auto keeps the source image’s shape.auto1:13:24:35:416:921:92:33:44:59:16
countintegerDefault 1
Variations of this brief, 1 to 4. Each is its own job and charge; ids lists them. A given seed is stepped for each.
seedinteger
Reuse a seed to stay close to an earlier result. Engines that take no seed say so in notes.
webhook_urlstring
A public https:// URL to send this job’s settle event to, signed like every webhook, on top of your account’s endpoints.

Response

On success (202): Started. Poll GET /v2/jobs/{id} for the result. The JSON is { "success": true, "data": … }, and inside data:

idstringRequired
The job. Read it at GET /v2/jobs/{id}. With count, the first of ids.
idsstring[]Required
Every job this call started, one per variation, in order.
statusstringRequired
The job’s status when it was accepted, usually queued.queuedprocessingsucceededfailedcanceled
charged_usdnumberRequired
What this call charged, every variation together, in US dollars.
replayedbooleanRequired
True when this is the stored answer to an earlier identical request with the same Idempotency-Key: nothing ran or was charged again. See Idempotency.
notesstring[]Required
What was accepted but not applied, in plain words: a setting that does not fit the engine, a seed it does not take. Empty when everything applied. When fewer variations started than count asked for, it says so here.
jobsobject[]
Only with ?wait: every job this call started, as GET /v2/jobs/{id} reads it, outputs included. A 200 means all of them finished in time; a 202 means some are still running: read each with GET /v2/jobs/{id}?wait=90 until it settles.
Fields of each item (10)
idstringRequired
The job’s id, as the create call answered it.
statusstringRequired
Where the job is. succeeded, failed and canceled are final: stop polling.queuedprocessingsucceededfailedcanceled
kindstringRequired
What the job makes: render, edit, erase, upscale, enhance, bg-remove, extend (an outpaint) or video.
enginestring | nullRequired
The engine’s mnml name for a render or an edit; null for an enhancement, an erase or a video.
outputsobject[]Required
The results, once the job has succeeded; empty before that, and on failure.
Fields of each item (3)
urlstringRequired
A signed link to the file at GET /v2/files/{id}. It needs no key.
mediastringRequired
Whether the file is an image or a video.imagevideo
expires_atstringRequired
When the link stops working, as an ISO 8601 time. Poll again for a fresh one, and download what you keep.
charged_usdnumberRequired
What the job charged when it started, in US dollars.
refunded_usdnumberRequired
What came back when the job failed or was cancelled, in US dollars; 0 otherwise.
errorobject | nullRequired
Why the job failed or stopped; null otherwise.
Fields (2)
codestringRequired
A stable code to branch on. See When a job fails.UNSAFE_CONTENTNO_CHANGECANCELEDRENDER_FAILED
messagestringRequired
The same in plain words, for a person.
created_atstringRequired
When the job was accepted, as an ISO 8601 time.
completed_atstring | nullRequired
When it reached a final status, as an ISO 8601 time; null while it runs.
Response headers (5)
X-Request-Idstring
This request, for support and the console log.
RateLimit-Limitinteger
Requests this key may make per minute.
RateLimit-Remaininginteger
Requests left in the current minute.
RateLimit-Resetinteger
Seconds until the current minute resets.
Idempotent-Replayedstring
Present, as true, when this is the stored answer to an earlier request.

Status codes

200
Finished, within ?wait: every job, with its outputs, is in jobs.
202
Started. Poll GET /v2/jobs/{id} for the result.
401
The API key is missing, malformed, unknown or revoked.
402
The balance does not cover this call.
409
The Idempotency-Key was reused for a different request, or is still in progress.
429
A limit was reached: requests per minute, or a daily or concurrency limit.
Other
Any other error, in the same envelope.

Every error answers { "success": false, "error": { "code", "message" } }. What each code means, and what to do, is on Errors.