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 accepted the run. You get a request_id and three URLs, and you collect the result when it is ready.
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 only add polling and ergonomics.
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.
Example: Nano Banana 2
This example queues an image generation on Nano Banana 2 (vertexai/gemini-3.1-flash-image) and collects it. The request body is the same one the synchronous snippet sends. Export your key as COMFY_API_KEY first.
Submit, follow, collect in one call
When you do want to wait but also want to show progress, the Python SDK folds the three steps into one:timeout= is a client-side bound. When it runs out, subscribe asks the server to cancel before raising, so you are not paying for a generation nobody will collect. Use submit instead when the request should outlive the caller.
Collect from another process
Both IDs address the request, so both are needed to rebuild a handle. No call is made until you use 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 400 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
Two parts of this contract are still under review before general availability, and either may change:- Whether a cancel on a request that already finished should answer
409instead of400ALREADY_COMPLETED. - Which status the result read answers for a request that was cancelled or timed out in the queue.
Next
Nano Banana 2
The synchronous call for the same model, with its full input and output schema.
Headers
Authentication, idempotency, request IDs, error buckets, retry pacing.