> ## Documentation Index
> Fetch the complete documentation index at: https://docs.comfy.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Read the queue state of one submitted request.

> The poll endpoint. It answers with the request's current state and never with the result, so a client can watch a long generation without transferring its output on every poll - the result is collected once, from the read below, when this says `COMPLETED`.



## OpenAPI

````yaml /router-openapi.yaml get /v2/models/{provider}/{model}/requests/{request_id}/status
openapi: 3.0.2
info:
  title: Comfy Router
  description: >-
    Comfy Router's public contract: the model catalog and the model-ID-addressed
    invocation routes, with the error buckets they return. Projected from the
    canonical Comfy API contract.
  version: '1.0'
servers:
  - url: https://api.comfy.org
security: []
tags:
  - name: Comfy Router
    description: Comfy Router's canonical, model-ID-addressed routes.
paths:
  /v2/models/{provider}/{model}/requests/{request_id}/status:
    get:
      tags:
        - Comfy Router
      summary: Read the queue state of one submitted request.
      description: >-
        The poll endpoint. It answers with the request's current state and never
        with the result, so a client can watch a long generation without
        transferring its output on every poll - the result is collected once,
        from the read below, when this says `COMPLETED`.
      operationId: getRouterModelRequestStatus
      parameters:
        - $ref: '#/components/parameters/RouterProvider'
        - $ref: '#/components/parameters/RouterModel'
        - $ref: '#/components/parameters/RouterQueueRequestId'
      responses:
        '200':
          description: >-
            OK - the request's current queue state. `status` is one of the three
            states; `queue_position` is present while the request is still
            `IN_QUEUE`; `error_type` is present only on a `COMPLETED` request
            that failed or was cancelled.
          headers:
            X-Comfy-Request-Id:
              $ref: '#/components/headers/RouterRequestIdHeader'
            Retry-After:
              $ref: '#/components/headers/RouterQueuePollAfterHeader'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouterQueueStatusResponse'
        '401':
          $ref: '#/components/responses/RouterRequestError'
        '403':
          $ref: '#/components/responses/RouterRequestError'
        '404':
          $ref: '#/components/responses/RouterRequestError'
        '410':
          $ref: '#/components/responses/RouterRequestError'
        '503':
          $ref: '#/components/responses/RouterRequestError'
        default:
          $ref: '#/components/responses/RouterRequestError'
      security:
        - BearerAuth: []
        - ApiKeyAuth: []
components:
  parameters:
    RouterProvider:
      name: provider
      in: path
      required: true
      description: >-
        Lowercase provider segment of the canonical `{provider}/{model}` model
        ID - the partner whose model is being run.
      schema:
        $ref: '#/components/schemas/RouterProviderSegment'
    RouterModel:
      name: model
      in: path
      required: true
      description: >-
        Lowercase model segment of the canonical `{provider}/{model}` model ID -
        the model to run within that provider.
      schema:
        $ref: '#/components/schemas/RouterModelSegment'
    RouterQueueRequestId:
      name: request_id
      in: path
      required: true
      description: >-
        The queued request to address - the `request_id` the submission returned
        in its body. It is not the submission's `X-Comfy-Request-Id`: that
        header carries the id of one HTTP call and addresses nothing, as the
        `RouterQueueRequestId` schema spells out.
      schema:
        $ref: '#/components/schemas/RouterQueueRequestId'
  headers:
    RouterRequestIdHeader:
      description: >-
        Server-generated identifier for this call, present on every Router
        response - success, 4xx and 5xx alike, because an error response is
        exactly when a user needs an id to quote in a support request. The same
        value is written into the call's usage/audit event, which is what lets a
        complaint about a charge be joined to the charge itself instead of
        searched for by timestamp.
      required: true
      schema:
        type: string
        format: uuid
        example: 6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21
    RouterQueuePollAfterHeader:
      description: >-
        Seconds to wait before polling this queued request again. It is Router's
        own estimate of when asking again is worth the round trip, and it moves
        with how far the request has actually got - a request at the back of the
        queue is told to wait longer than one already running.
      required: false
      schema:
        type: integer
        minimum: 1
        example: 3
    RouterErrorTypeHeader:
      description: >-
        Coarse, machine-readable bucket for the failure, set by Router on every
        error response. It carries the same value as
        `RouterErrorResponse.error_type`, and on the `422` it is the only
        machine-readable bucket, because that body is the FastAPI `detail[]`
        shape and has no `error_type` field of its own. A client can therefore
        branch on this header alone, before deciding which of the two Router
        error bodies it received.
      required: true
      schema:
        $ref: '#/components/schemas/RouterErrorType'
  schemas:
    RouterQueueStatusResponse:
      type: object
      description: >-
        One queued request's current state, composed with the same three URLs
        the submission returned.
      allOf:
        - $ref: '#/components/schemas/RouterQueueUrls'
        - $ref: '#/components/schemas/RouterQueueStatusFields'
    RouterProviderSegment:
      type: string
      description: >-
        Lowercase `provider` segment of the canonical `{provider}/{model}` model
        ID - the partner whose model is being addressed. The invocation route's
        `provider` path parameter and a catalog entry's `provider` field both
        reference this one schema, which is what keeps the listed IDs and the
        accepted IDs from drifting apart.
      pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$
      maxLength: 64
      example: bfl
    RouterModelSegment:
      type: string
      description: >-
        Lowercase `model` segment of the canonical `{provider}/{model}` model ID
        - the model to run within that provider. Shared by the invocation
        route's `model` path parameter and a catalog entry's `model` field, for
        the same no-drift reason as `RouterProviderSegment`.
      pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$
      maxLength: 128
      example: flux-2-pro
    RouterQueueRequestId:
      type: string
      format: uuid
      x-go-type: string
      pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
      maxLength: 36
      description: >-
        Identifier of one queued Router request - the handle a caller polls,
        cancels and collects a result by.
      example: 6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21
    RouterQueueUrls:
      type: object
      description: >-
        The three URLs that address the rest of one queued request's lifetime,
        returned on every response that carries a live handle so a client never
        composes a queue URL itself.
      properties:
        status_url:
          type: string
          format: uri
          description: Absolute URL of this request's status read.
          example: >-
            https://api.comfy.org/v2/models/bfl/flux-pro-1.1/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21/status
        response_url:
          type: string
          format: uri
          description: Absolute URL this request's result is collected from.
          example: >-
            https://api.comfy.org/v2/models/bfl/flux-pro-1.1/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21
        cancel_url:
          type: string
          format: uri
          description: Absolute URL a cancellation is asked for at.
          example: >-
            https://api.comfy.org/v2/models/bfl/flux-pro-1.1/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21/cancel
      required:
        - status_url
        - response_url
        - cancel_url
    RouterQueueStatusFields:
      type: object
      description: >-
        The half of `RouterQueueStatusResponse` that is not the URL block: one
        queued request's identity, its current state, and - when that state is
        terminal and the run did not succeed - the coarse bucket saying why.
      properties:
        request_id:
          $ref: '#/components/schemas/RouterQueueRequestId'
        status:
          $ref: '#/components/schemas/RouterQueueStatus'
        queue_position:
          $ref: '#/components/schemas/RouterQueuePosition'
        error_type:
          allOf:
            - $ref: '#/components/schemas/RouterErrorType'
          description: >-
            Present only on a `COMPLETED` request that did not succeed, carrying
            the same coarse bucket the result read puts on `X-Comfy-Error-Type`
            when it returns that failure. It is what distinguishes a terminal
            request that succeeded from one that failed or was cancelled - there
            is no separate terminal status for either - and it is absent on
            success rather than null, so branch on its presence.
      required:
        - request_id
        - status
    RouterErrorResponse:
      type: object
      description: >-
        Router's request-level error body: what is returned when the request
        never reached the model, or failed for a reason the model itself did not
        report - auth, quota, an unknown model ID, or provider transport. A
        model-level validation failure has its own shape,
        `RouterValidationErrorResponse`, because flattening a FastAPI `detail[]`
        array into this `detail` string would destroy the per-field granularity
        an SDK branches on.
      properties:
        detail:
          type: string
          description: >-
            Human-readable description of the failure, safe to surface to an end
            user. Not machine-parsed - branch on `error_type` instead.
        error_type:
          $ref: '#/components/schemas/RouterErrorType'
        upstream_detail:
          type: string
          description: >-
            A bounded, sanitized reason the model provider gave for rejecting
            the request, present only when `error_type` is `invalid_input` and
            `X-Comfy-Upstream-Status` is a provider `4xx` or `2xx` - i.e. the
            provider refused the request as malformed and said why. Usually that
            status is a `4xx`; it is a `2xx` for a provider that reports a
            rejected generation inside a success envelope (a BytePlus
            failed-task poll is HTTP `200` with the reason in its body). Absent
            on every other failure, including provider `5xx`, transport
            failures, content-policy refusals and any refusal Router raised
            about itself. It mirrors the `X-Comfy-Upstream-Detail` header.
        refusal_subject:
          type: string
          description: >-
            Which input or output a content-policy refusal was about, as a
            Router-level closed vocabulary: `input`, `output`, `input_text`,
            `input_image`, `input_video`, `input_audio`, `output_text`,
            `output_image`, `output_video`, `output_audio`. The bare `input` /
            `output` values name the side when the provider did not name a
            modality. Present only when `error_type` is
            `content_policy_violation` and the provider named the refused
            subject with a machine-readable code; absent otherwise. Never
            provider text. Named today for BytePlus, Runway, BFL, Gemini, Veo,
            Vertex, xAI and Wan refusals; a provider whose refusal does not say
            which side it was about leaves it absent. It mirrors the
            `X-Comfy-Refusal-Subject` header.
      required:
        - detail
        - error_type
    RouterQueueStatus:
      type: string
      description: >-
        The state of a queued Router request. It has exactly three values, and
        unlike `RouterErrorType` this one is a closed `enum`, because the two
        schemas are closed in opposite directions on purpose. `RouterErrorType`
        classifies failures and its set is expected to grow, so a generated
        client that hard-rejected an unrecognised bucket would fail hardest
        exactly when something had already gone wrong. This one is a lifecycle,
        and a lifecycle with a fourth state added later is a breaking change to
        every polling loop written against it whether it is declared as an enum
        or not - so it is declared as one, and the constraint is stated where a
        client can see it.
      enum:
        - IN_QUEUE
        - IN_PROGRESS
        - COMPLETED
      example: IN_QUEUE
    RouterQueuePosition:
      type: integer
      minimum: 0
      description: >-
        How many requests are ahead of this one in the queue, at the instant the
        response was composed. Zero means this request is at the front.
      example: 3
    RouterErrorType:
      type: string
      description: >-
        Coarse, machine-readable bucket for a Router failure, mirrored on the
        `X-Comfy-Error-Type` response header so a caller can branch without
        parsing the body. The set is closed at nineteen values: the six
        request-level buckets `invalid_input`, `content_policy_violation`,
        `provider_error`, `provider_timeout`, `insufficient_credits` and
        `model_not_found`, plus the transport-level `unauthorized`, `forbidden`,
        `concurrency_limit_exceeded`, `client_disconnected`, `internal_error`,
        `deadline_exceeded`, `not_enabled`, `service_unavailable`,
        `rate_limited`, `cancelled`, `queue_timeout`, `request_not_found` and
        `queue_backlog_full`. Closed describes the set as documented today, not
        a bound that holds forever: the set is expected to grow, which is why
        this is deliberately a plain string and not an `enum`, so a client must
        treat an unrecognised value as `internal_error` rather than switch
        exhaustively over the list above and break on the next addition.
      example: invalid_input
      x-comfy-error-types:
        - value: invalid_input
          tier: request
          meaning: >-
            The request was rejected before it reached the model - a malformed
            body, a malformed or expired pagination cursor, an input the model's
            own schema does not accept, or an `Idempotency-Key` that cannot
            serve this request (already used for a different request - the
            method, the path and query, or the body differ - or already consumed
            by a call whose response cannot be replayed). Sent with `409` in the
            key cases and with `400`/`422` in the others; the status says which,
            and the key cases are the ones answered by using a new key rather
            than by editing the request.
        - value: content_policy_violation
          tier: request
          meaning: >-
            The provider refused the request on content-policy grounds. The
            refusal is deterministic: re-sending the same input will be refused
            again.
        - value: provider_error
          tier: request
          meaning: >-
            The partner provider reported a failure of its own, or returned a
            response Router could not interpret as a result.
        - value: provider_timeout
          tier: request
          meaning: >-
            The partner provider did not answer within its deadline. This bucket
            is the provider timing out and never Router's own server deadline,
            which is reported as `deadline_exceeded` - the two share `504` and
            are separated because they name different causes: this one says the
            partner failed, that one says Comfy stopped holding the connection.
        - value: insufficient_credits
          tier: request
          meaning: The calling workspace does not have enough credits to run the model.
        - value: model_not_found
          tier: request
          meaning: >-
            The `{provider}/{model}` ID names no model Router can run; an
            unknown provider lands here too. `detail` carries up to three
            suggestions drawn from the models the caller is entitled to see.
            This includes a catalogued ID the provider does not currently serve
            for Comfy; the response then carries no suggestions.
        - value: unauthorized
          tier: transport
          meaning: The request carried no usable credential.
        - value: forbidden
          tier: transport
          meaning: >-
            The credential is valid but is not entitled to this model or this
            operation.
        - value: concurrency_limit_exceeded
          tier: transport
          meaning: >-
            The workspace already has as many calls in flight as it is allowed;
            retry once one of them finishes. It carries one further condition on
            the run route, on a `409` rather than the `429` above: another call
            is already in flight for the `Idempotency-Key` this request
            presented. Re-send the same key after `Retry-After` seconds to
            collect that call's result.
        - value: client_disconnected
          tier: transport
          meaning: >-
            The caller closed the connection before Router could return a
            result. It is logged rather than delivered - there is no socket left
            to write it to - and it is an attribution, not a billing outcome: a
            provider generation that completed is billed regardless of whether
            the caller received the response.
        - value: internal_error
          tier: transport
          meaning: >-
            Router itself failed. It is also the value a client should treat any
            unrecognized bucket as, so a later addition to the set does not
            break a client generated before it.
        - value: deadline_exceeded
          tier: transport
          meaning: >-
            Comfy stopped holding the connection at its own configured bound
            before an answer arrived. It shares `504` with `provider_timeout`
            and the pair says which side ran out of time; this one is Comfy's
            own bound, so nothing about the request was rejected and the same
            request may be retried. It says nothing about the charge: a provider
            generation that completed is billed regardless of whether the caller
            received the response. Retry it with the same `Idempotency-Key`:
            when the provider had already accepted the generation, the retry
            collects that generation rather than dispatching another, and a
            `Retry-After` on the `504` says when to ask.
        - value: not_enabled
          tier: transport
          meaning: >-
            Comfy Router is not switched on for this caller yet. Nothing about
            the request is wrong and the model exists, which is why this is not
            `model_not_found`; it shares `403` with `forbidden` and is not the
            same thing, because `forbidden` is an entitlement decision about the
            caller while this is a state of the rollout. It is terminal: do not
            retry, and do not treat it as an outage. The one exception to "about
            the caller" is the queued submit, which also answers `not_enabled`
            for a model whose partner answers a generation directly as bytes:
            that model cannot yet be queued, so it is the model and not the
            caller that is refused, nothing is queued or charged, and the
            synchronous route `POST /v2/models/{provider}/{model}` runs it
            instead.
        - value: service_unavailable
          tier: transport
          meaning: >-
            A service Comfy Router depends on is temporarily unavailable and the
            caller did nothing wrong. Retry it with backoff: it is the one
            bucket here whose condition clears on its own, without the caller
            changing the request and without a concurrency slot freeing, which
            is what distinguishes it from the other retryable answers
            (`concurrency_limit_exceeded`, `deadline_exceeded`). It is separate
            from `internal_error` - which is a `500` and means Router itself
            failed - so a client can tell "come back shortly" from "this call is
            not going to work".
        - value: rate_limited
          tier: transport
          meaning: >-
            The caller has spent an allowance measured over a window and must
            wait for that window to roll. It shares `429` with
            `concurrency_limit_exceeded` and is not the same thing: that one
            clears the moment one of the caller's own in-flight calls finishes,
            so retrying in seconds is right, whereas nothing the caller does
            drains this one early. `detail` names the window.
        - value: cancelled
          tier: transport
          meaning: >-
            A queued request was withdrawn — through the cancel route, or by an
            operator — before it produced a result; it is terminal, and it is
            not by itself a statement about the charge. Cancelling stops Comfy
            waiting and it does not always stop the partner working, so a
            request cancelled while it was still `IN_QUEUE` was never dispatched
            and cannot be charged, whereas one cancelled after it was admitted
            may still be charged — a partner generation that completes is
            charged whether or not anyone collected it, which is why the cancel
            route calls the ask a request rather than a guarantee. It is not
            `client_disconnected`: that one says nobody is listening any more
            while a generation may still be running and billable, whereas this
            says the request itself was withdrawn. A caller polling the status
            read sees `200` with this in the body, because the request ended and
            so the read succeeded. Collecting the result of such a request
            answers `409` in Router's error envelope: that read also worked, and
            what it found is a request whose terminal state - the caller's own
            decision - leaves nothing to return. It is deliberately not `410`,
            which on that route means the result aged out of its retention
            window, and not a `5xx`, which would report the caller's own
            cancellation as a Router fault and read to a polling client as worth
            retrying.
        - value: queue_timeout
          tier: transport
          meaning: >-
            A queued request waited past its queue timeout without ever being
            admitted. Terminal, unbilled, and it never took a concurrency slot —
            the job never reached a provider. Deliberately not
            `deadline_exceeded`, which is the synchronous route's connection
            bound: a `deadline_exceeded` generation may be running and billable,
            whereas this one provably never started. It is returned under `504`,
            which it shares with `provider_timeout` and `deadline_exceeded`
            because a status can only say that a clock ran out; which clock -
            the partner's, Comfy's connection bound, or the queue's admission
            bound - is what `error_type` carries. The request itself is
            terminal, so submit a new one rather than re-reading this one.
        - value: request_not_found
          tier: transport
          meaning: >-
            The `request_id` names no request of the caller's under this model.
            It is the second of the two conditions the queued reads' `404`
            covers; the first is the `{provider}/{model}` ID resolving to no
            partner model, which is `model_not_found` and carries fuzzy model
            suggestions. It also covers the right-id / wrong-model URL the path
            shape refuses, and it is deliberately indistinguishable from a
            request in another workspace, so a probe with a guessed id learns
            nothing. A request that has merely aged out of its retention window
            is `410`, not this.
        - value: queue_backlog_full
          tier: transport
          meaning: >-
            The caller already has too many queued requests waiting to run, so
            this submit was refused. It shares `429` with
            `concurrency_limit_exceeded` and is not the same thing: that one is
            the synchronous route's answer for too many calls in flight at once,
            whereas the queue accepts a submit at that limit and parks it, and
            this bucket is the separate bound on how many a caller may leave
            waiting so that parking cannot mean enqueuing without end. It clears
            as the caller's own queued requests finish, so retry once some of
            them complete.
  responses:
    RouterRequestError:
      description: >-
        A Router request-level failure - the request never reached the model, or
        failed for a reason the model itself did not report. The body is
        `RouterErrorResponse` and the bucket is repeated on
        `X-Comfy-Error-Type`.
      headers:
        X-Comfy-Error-Type:
          $ref: '#/components/headers/RouterErrorTypeHeader'
        X-Comfy-Request-Id:
          $ref: '#/components/headers/RouterRequestIdHeader'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RouterErrorResponse'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Bearer token authentication. Normally a Firebase or Cloud JWT. A
        'comfyui-' prefixed API key is also accepted in this header: the prefix
        classifies the value as an API key and it is validated exactly as if it
        had been sent in X-API-Key.
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        API key authentication. Send the key in the X-API-Key header; keys are
        prefixed with 'comfyui-' and are generated from user account settings.
        The same 'comfyui-' key is also accepted in Authorization: Bearer (see
        BearerAuth), and when both headers carry a key, X-API-Key wins.

````