Jobs and polling
Every create call returns a job. One endpoint tells you when any of them is done.
GET/v2/jobs/{id}Headers
AuthorizationstringRequiredBearer mk_live_…: your API key. See Authentication.
Path parameters
idstringRequired- The job id a create call answered with.
Query parameters
waitinteger- Hold the answer up to this many seconds (1 to 90) until the job settles; it answers as soon as it does. Call again while status is queued or processing.
Response
On success (200): The job. The JSON is { "success": true, "data": … }, and inside data:
idstringRequired- The job’s id, as the create call answered it.
statusstringRequired- Where the job is.
succeeded,failedandcanceledare final: stop polling.queuedprocessingsucceededfailedcanceled kindstringRequired- What the job makes:
render,edit,erase,upscale,enhance,bg-remove,extend(anoutpaint) orvideo. enginestring | nullRequired- The engine’s mnml name for a render or an edit;
nullfor 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;
nullotherwise.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;
nullwhile it runs.
Response headers (4)
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.
Status codes
- 200
- The job.
- 401
- The API key is missing, malformed, unknown or revoked.
- 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.
Job statuses
| Status | Meaning |
|---|---|
| queued | Accepted and waiting for capacity. |
| processing | Rendering now. |
| succeeded | Done. outputs lists the results. |
| failed | Did not finish. error says why, and credits_refunded what came back. |
| canceled | Stopped before it delivered, and refunded. |
Waiting for a job
Add ?wait=90 to the read: it answers as soon as the job is succeeded, failed or canceled, or with the job as it stands after 90 seconds. Call it again while the status is queued or processing. Reading a job is free. A render started with ?wait often needs no read at all.
while :; do job=$(curl -s "https://api.mnml.ai/v2/jobs/10482?wait=90" -H "Authorization: Bearer $MNML_API_KEY") echo "$job" | grep -qE '"status":"(queued|processing)"' || breakdoneecho "$job"Without wait, poll every two to five seconds. A video takes minutes: for video, or for many jobs at once, a webhook beats waiting.
Following a job as a stream
GET /v2/jobs/{id}/events holds one connection open for as long as the job runs, up to 10 minutes, so a render that takes two or three minutes needs no loop at all. It is a Server-Sent Events stream: a job event with the job as it stands, another each time it changes, then one last event and the stream closes. A job that has already settled when the stream opens gets the done event alone. Every event carries JSON, and a job or done event is exactly what GET /v2/jobs/{id} answers.
| Event | Meaning |
|---|---|
| job | The job while it is queued or processing: first as it stands, then on each change. |
| done | The last event: the job settled, succeeded, failed or canceled. outputs holds the results. |
| timeout | The last event after 10 minutes: the job as it stands. Open the stream again. |
| error | The last event when the job could not be read: { code, message }, with code READ_FAILED. The job still runs; read it with GET /v2/jobs/{id}. |
curl -N "https://api.mnml.ai/v2/jobs/10482/events" -H "Authorization: Bearer $MNML_API_KEY" event: jobdata: {"id":"10482","status":"processing","outputs":[],…} event: donedata: {"id":"10482","status":"succeeded","outputs":[{"url":"…","media":"image",…}],…}While nothing changes, a : ping comment arrives every 15 seconds to keep the connection alive; SSE clients skip it. An unknown job is a plain 404, before any stream starts. Hanging up closes the stream, never the job. An account can hold 20 streams and ?wait calls open at once, counted together across our servers; a stream past that is a 429. For many jobs, a webhook is the better fit. When we restart a server for a deploy, an open stream ends with timeout: open it again.
Outputs
Each output has a url, its media (image or video) and an expires_at. The link is signed and needs no key, so you can hand it to a browser; it lasts one to two hours, and a fresh poll gives a fresh one. A job and its file are kept for 7 days, then deleted, and never appear in your mnml library. Download what you want to keep.
Images come without a watermark once you have added funds to your API balance. Before that, they carry the mnml watermark, as Studio downloads do on an account that has never paid.
When a job fails
| error.code | Meaning |
|---|---|
| RENDER_FAILED | The render did not complete. It is refunded; try again. |
| UNSAFE_CONTENT | The request cannot be rendered under the content policy. |
| NO_CHANGE | The engine returned the image unchanged. |
| CANCELED | The job was cancelled before it delivered. |
Download an output
GET /v2/files/{id} is the link each output’s url already holds: fetch it as it is. It takes no key, since the signature in the link is the credential, so a browser or an <img> tag can load it until expires_at. An expired or altered link answers 404; poll the job again for a fresh one.
Path parameters
idstringRequired- The job id a create call answered with.
Query parameters
expstringRequired- When the link expires (part of the link).
sigstringRequired- The signature (part of the link).
Response
On success (200): The file: an image, watermarked on plans that mark downloads, or a video. The body is the file itself, as image/jpeg, image/png, image/webp, video/mp4.
Response headers (1)
X-Request-Idstring- This request, for support and the console log.
Status codes
- 200
- The file: an image, watermarked on plans that mark downloads, or a video.
- 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.