Jobs, waiting and limits
Every generation is a job on a shared render queue. One GPU renders one job at a time. Accounts take turns: when several accounts have work waiting, the next job comes from the account served least recently, and each account's own jobs run in the order it submitted them. A long batch from one account or agent therefore never blocks everyone else, and how long you wait depends on the job itself and on what is ahead of it.
Lifecycle #
| Status | Meaning | Useful fields |
|---|---|---|
queued |
Accepted and charged, waiting for the renderer | queue_position (1 = next), eta_seconds, phase |
running |
Rendering | progress (0 to 1), phase (what is happening now), eta_seconds |
succeeded |
Finished | outputs with download links |
failed |
Did not finish; refunded in full | error, refunded |
cancelled |
Stopped by you or interrupted; refunded in full | refunded |
succeeded, failed and cancelled are final.
Typical times #
| Job | Render time once it starts |
|---|---|
| Image | about 10 seconds at 1 MP, 25 at 2 MP, a minute at 4 MP; more with references |
| Edit | about 15 seconds at 1 MP, 40 at 2 MP; more with references |
| Video | 2 to 3 minutes; longer and larger clips take longer |
The first image after a video takes longer while the image models load, so a short run of image jobs is kept together before switching back to video. get_service_status (REST: GET /meta) reports whether rendering is online, whether new work is being accepted, how many jobs are queued across the service and the estimated wait. eta_seconds on a job is estimated from its size and length, corrected by recent measured render times, plus the jobs ahead of it; treat it as an estimate.
A job that runs far longer than its estimate is stopped and refunded, so a stuck render cannot hold the GPU.
Waiting over MCP #
No tool call needs to stay open for the whole render.
generate_image,edit_imageandgenerate_videosubmit the job and then wait up towait_seconds(default 45, maximum 300,0to return at once).- They return the job as it stands. If it finished, the outputs are there. If not,
statusisqueuedorrunningandnexttells you to wait. wait_for_jobwaits again for up totimeout_seconds. Call it until the status is final.get_jobchecks without waiting.
generate_video ... -> job J, status "running", progress 0.31, eta_seconds 95
wait_for_job job_id J -> status "running", progress 0.74, eta_seconds 40
wait_for_job job_id J -> status "succeeded", outputs [...]While a call is open, the server sends MCP progress notifications (to clients that pass a progress token) and keep-alive frames, so connections through proxies stay up.
Choosing a wait. The defaults stay under the 60-second tool timeout many clients enforce. If your client allows longer calls, use eta_seconds plus a margin, up to 300. See the timeout table in Connect your agent.
If a call times out on the client side, the job is unaffected: it keeps its place and its credits stay reserved. Do not submit it again. Find it with list_jobs (active_only: true) and resume with wait_for_job.
There is no automatic de-duplication. Submitting the same request twice makes, and charges for, two jobs.
Waiting over REST #
POST /jobs returns 202 with the job at once. Then either poll GET /jobs/:id every few seconds, or hold one GET /events connection (Server-Sent Events) and react to job events. See the REST API reference.
Cancelling #
cancel_job (REST: POST /jobs/:id/cancel) stops a queued or running job and refunds it. A running render stops within a few seconds.
Limits #
| Limit | Value |
|---|---|
| Generations queued or running at once, per account | reported by get_account as concurrent_limit (3 by default) |
| Work waiting on the shared queue | new jobs are refused while the queue holds more than about 30 minutes of work (less for trial accounts) |
| MCP requests | 300 per minute |
| REST requests | 600 per minute per address; job creation 30 per minute |
| Prompt length | 4000 characters (8000 with the pro model) |
| References per generation | 4 |
| Upload size | 25 MB per image (PNG, JPEG, WebP) |
| Download links from MCP | valid for 1 hour; get_asset issues a new one |
| Upload links from MCP | valid for 15 minutes |
Over a limit you get 429; wait and retry. too_many_jobs clears as soon as one of your jobs finishes.
When the shared queue is full or rendering is offline, new jobs get 503 with queue_full or renderer_offline. Nothing is charged. Wait a few minutes before submitting again (check accepting_new_jobs in get_service_status); do not retry in a tight loop. Jobs that were already accepted stay queued and run when the renderer is back, or are refunded if it stays offline.
Errors #
Over MCP, a failed tool call returns isError: true with a JSON body {"error": {"code", "message", "hint"}}; the hint says what to do next. Over REST the same codes come back as {"error": {"code", "message"}} with the HTTP status shown.
| Code | Status | Meaning |
|---|---|---|
invalid_request |
400 | A parameter is missing or out of range; the message names it |
invalid_size |
400 | Width and height must be given together and fit the size limits (sequence frames are smaller than standalone images) |
invalid_sprite |
400 | Sprite size or options out of range |
not_a_sprite |
400 | The sprite-set tools only take sprites (and, for links, sprite sheets) |
sheet_too_large |
400 | A sprite sheet would be wider or taller than 4096 px |
invalid_style_kit |
400 | Style kit fields out of range |
style_kit_not_found |
404 | No such style kit on this account |
too_many_style_kits |
400 | The account already has the maximum number of style kits |
not_a_sequence |
400 | Frames and video need a sequence; that id is the image library |
invalid_frames, clip_too_long |
400 | Clip length out of range for this frame size |
no_start_frame |
400 | The step has no selected start frame yet |
no_end_frame |
400 | First-and-last-frame video needs a selected end frame |
start_fixed |
400 | From the second step on, the start frame is the previous take's last frame |
invalid_source |
400 | That image cannot be edited in this way; the message says what to do instead |
size_mismatch |
400 | The frame does not have the sequence's frame size |
unknown_reference |
400 | An @label in the prompt is not a reference of this sequence |
too_many_references |
400 | More than 4 references in one prompt |
clip_stale |
400 | That take was rendered from a different start frame than the one selected |
unauthenticated, invalid_api_key |
401 | Missing, wrong or revoked API key |
insufficient_credits |
402 | The balance does not cover the job |
forbidden, account_disabled |
403 | The account may not do this |
project_not_found, segment_not_found, asset_not_found, job_not_found |
404 | No such item on this account |
segment_accepted |
409 | The step is accepted; work on the open step or reopen it |
asset_in_use, asset_locked |
409 | The item is selected or belongs to an accepted step |
jobs_active |
409 | Wait for or cancel the running generations first |
job_finished |
409 | The job already finished and cannot be cancelled |
too_many_jobs |
429 | The concurrency limit is reached |
queue_full |
503 | The shared render queue is at capacity; nothing was charged |
renderer_offline |
503 | Rendering is offline; nothing was charged |
A job that fails while rendering reports a general message (The render failed on our side. Your credits were refunded.). It is safe to submit it again; if failures repeat, check get_service_status.