POST /v2/models/{provider}/{model} holds the connection until the model finishes. Queued delivery takes the same model ID and the same native request body, but returns as soon as Router has admitted the run. You get a request_id back at once and collect the result when it is ready, from the same process or another one.
Use the queue when a generation can outlast the connection you can hold, when a web request has to return now, when you submit in one process and collect in another, or when you want many generations in flight at once. Ordering, admission, retries, timeouts, billing and expiry are all decided on the server. The SDKs add polling and ergonomics on top, nothing else.
Two delivery modes, one request
The SDKs (
comfy-sdk and @comfyorg/sdk, 0.3.0 or later) expose the queue as three methods next to run:
submit(model, body)sends the request and returns a handle at once. The handle carriesstatus(),get(),cancel()and an event iterator (iter_events()in Python,events()in TypeScript).subscribe(model, body, ...)is submit, poll and collect in one call, with a progress callback.handle(model, request_id)rebuilds a handle in another process from the two IDs, with no call made.
/v2/models/{provider}/{model}/requests/{request_id}.
The four routes
status is one of IN_QUEUE, IN_PROGRESS or COMPLETED. There is no separate failed or cancelled status: a request that did not succeed is COMPLETED carrying an error_type, so branch on the presence of that field, not on a fourth status value. The SDKs do this for you: get() raises or rejects with the typed Router error instead of handing the failure back as a result.
The API reference carries the full contract for each route.
Queue a request
This queues the same request the quickstart sends and collects the image. Export your key asCOMFY_API_KEY first.
Follow progress and collect in one call
When you do want to wait but also want to show progress,subscribe folds submit, poll and collect into one call:
subscribe makes one best-effort cancel before raising, so you are not paying for a generation nobody will collect. Use submit when the request should outlive the caller.
Collect from another process
Store therequest_id next to the model ID. Both are needed to rebuild a handle, and no call is made until you use it.
Check status or cancel
status() is one poll and returns the current state. cancel() asks the server to stop a request that has not finished. It is a request, not a guarantee: a run already on the wire at the partner may complete anyway, and the next status() is what is true.
Async Python
AsyncComfy mirrors every name, argument and argument order. There is no submit_async, for the same reason there is no run_async.
Errors the SDKs raise
A request that finished without succeeding is reported asCOMPLETED with an error_type. get() and subscribe() turn that into the typed Router error for the bucket: the classes in comfy_sdk.router_exceptions in Python, and routerErrors.* in TypeScript. The event iterator does not raise for that case, because it is a view of the queue’s progress: a completion carrying an error_type is yielded as the last observation, and get() is what collects. A 403 not_enabled on submit arrives as NotEnabled and is terminal, so the SDKs do not retry it.
What the responses look like
Submit,201. status is always IN_QUEUE at this point. The three URLs are absolute and are authenticated with the same key as the submit.
request_id is also the value of the submit’s X-Comfy-Request-Id header. Keep the model ID next to it: the request is addressed by both.
Status, 200. The same shape, with the current state. queue_position counts the requests ahead of yours and reaches 0 when the run is at the front. Retry-After on this response is Router’s estimate of when polling again is worth the round trip. It is a hint, not a bound, and a request at the back of the queue is told to wait longer than one already running. Polling faster learns nothing earlier and spends your own rate-limit allowance.
COMPLETED with an error_type, carrying the same coarse bucket the result read puts on X-Comfy-Error-Type. The field is absent on success rather than null.
200 carries the model’s own native output, byte for byte what the synchronous route returns for the same model and input, under the provider’s own Content-Type. While the request is not finished the read answers 202 with the status body above, so a client that only polls the result URL parses one type. A request that failed comes back as an error response with X-Comfy-Error-Type set, the same buckets as the synchronous route.
Cancel. 202 with CANCELLATION_REQUESTED means the ask was accepted, not that the run has stopped. A run already on the wire at the partner may complete anyway, and a partner generation that completes is charged whether or not anyone collects it. Read the status afterwards: a cancellation that took effect shows as COMPLETED with error_type: cancelled. A request that aged out before it could run shows queue_timeout the same way. A request that had already finished answers 409 with ALREADY_COMPLETED.
Idempotency and billing
- Same charge as the synchronous route. You are billed when the provider bills Comfy. Time spent waiting in the queue is not charged.
- One
Idempotency-Keyper submit. The SDK mints a fresh key persubmitcall, so two deliberate submits of the same input are two requests. A retry of the same call under the same key does not queue a second run: it returns the original handle withIdempotent-Replayed: true. Pass your own key when a lost response could have cost you therequest_id. See Headers. - Results expire. A finished request is kept for 24 hours after it completes. After that, the status and result reads answer
410and the result is gone. Collect promptly and download any asset URLs the output carries. - Polls are requests too. Status and result reads count towards the per-caller request rate. Honour
Retry-Afterrather than polling on a fixed short interval.
Errors
Every error response carries
X-Comfy-Request-Id. Quote it when you contact support.
Preview notes
The routes, fields and SDK methods on this page are the contract the preview runs against. Progress events, webhooks and priority are not part of it.Next
Quickstart
The synchronous call for the same model, from nothing to an image.
Models
Every model page has the queued snippet for its own model and body.
Headers
Authentication, idempotency, request IDs, error buckets, retry pacing.
API reference
The four queue routes, field by field.