Skip to main content
Experimental API: This API is experimental and subject to change. Endpoints, request/response formats, and behavior may be modified without notice.

Comfy Cloud API

The Comfy Cloud API provides programmatic access to run workflows on Comfy Cloud infrastructure. The API is compatible with local ComfyUI’s API, making it easy to migrate existing integrations.
Subscription Required: Running workflows via the API requires an active Comfy Cloud subscription. See pricing plans for details.

Base URL

https://cloud.comfy.org

Authentication

All API requests require an API key passed via the X-API-Key header.

Getting an API Key

1

Visit https://platform.comfy.org/login and Log In

Please visit https://platform.comfy.org/login and log in with the corresponding accountVisit Platform Login Page
2

Click `+ New` in API Keys to Create an API Key

Click + New in API Keys to create an API KeyCreate API Key
3

Enter API Key Name

Enter API Key Name
  1. (Required) Enter the API Key name,
  2. Click Generate to create
4

Save the Obtained API Key

Obtain API Key
Since the API Key is only visible upon first creation, please save it immediately after creation. It cannot be viewed later, so please keep it safe. Please note that you should not share your API Key with others. Once it leaked, you can delete it and create a new one.
Keep your API key secure. Never commit it to version control or share it publicly.

Using the API Key

Pass your API key in the X-API-Key header with every request:
curl -X GET "https://cloud.comfy.org/api/user" \
  -H "X-API-Key: $COMFY_CLOUD_API_KEY"

Core Concepts

Workflows

ComfyUI workflows are JSON objects describing a graph of nodes. The API accepts workflows in the “API format” (node IDs as keys with class_type, inputs, etc.) as produced by the ComfyUI frontend’s “Save (API Format)” option.

Jobs

When you submit a workflow, a job is created. Jobs are executed asynchronously:
  1. Submit workflow via POST /api/prompt
  2. Receive a prompt_id (job ID)
  3. Monitor progress via WebSocket or poll for status
  4. Retrieve outputs when complete

Outputs

Generated content (images, videos, audio) is stored in cloud storage. Output files can be downloaded via the /api/view endpoint, which returns a 302 redirect to a temporary signed URL.

Quick Start

Here’s a complete example showing how to submit a workflow, monitor its progress, and retrieve outputs:

Step 1: Submit a Workflow

curl -X POST "https://cloud.comfy.org/api/prompt" \
  -H "X-API-Key: $COMFY_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": '"$(cat workflow_api.json)"'}'

Step 2: Monitor Job Progress

You can monitor job completion using either polling or WebSocket for real-time updates.

Option A: Polling (Simple)

Job Status Values: The API returns one of the following status values:
StatusDescription
pendingJob is queued and waiting to start
in_progressJob is currently executing
completedJob finished successfully
failedJob encountered an error
cancelledJob was cancelled by user
# Poll for job completion
curl -X GET "$BASE_URL/api/job/{prompt_id}/status" \
  -H "X-API-Key: $COMFY_CLOUD_API_KEY"

# Response examples:
# {"status": "pending"}      - Job is queued
# {"status": "in_progress"}  - Job is currently running
# {"status": "completed"}    - Job finished successfully
# {"status": "failed"}       - Job encountered an error
# {"status": "cancelled"}    - Job was cancelled

Option B: WebSocket (Real-time Progress)

For real-time progress updates and to collect output metadata:
async function listenForCompletion(
  promptId: string,
  timeout: number = 300000
): Promise<Record<string, any>> {
  const wsUrl = `wss://cloud.comfy.org/ws?clientId=${crypto.randomUUID()}&token=${API_KEY}`;
  const outputs: Record<string, any> = {};

  return new Promise((resolve, reject) => {
    const ws = new WebSocket(wsUrl);
    const timer = setTimeout(() => {
      ws.close();
      reject(new Error(`Job did not complete within ${timeout / 1000}s`));
    }, timeout);

    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      const msgType = data.type;
      const msgData = data.data ?? {};

      // Filter to our job
      if (msgData.prompt_id !== promptId) return;

      if (msgType === "executing") {
        const node = msgData.node;
        if (node) {
          console.log(`Executing node: ${node}`);
        } else {
          console.log("Execution complete");
        }
      } else if (msgType === "progress") {
        console.log(`Progress: ${msgData.value}/${msgData.max}`);
      } else if (msgType === "executed" && msgData.output) {
        outputs[msgData.node] = msgData.output;
      } else if (msgType === "execution_success") {
        console.log("Job completed successfully!");
        clearTimeout(timer);
        ws.close();
        resolve(outputs);
      } else if (msgType === "execution_error") {
        const errorMsg = msgData.exception_message ?? "Unknown error";
        clearTimeout(timer);
        ws.close();
        reject(new Error(`Execution error: ${errorMsg}`));
      }
    };

    ws.onerror = (err) => {
      clearTimeout(timer);
      reject(err);
    };
  });
}

// Wait for completion and collect outputs
const outputs = await listenForCompletion(promptId);
See WebSocket Reference for detailed message types and handling binary preview images.

Step 3: Download Outputs

Once the job completes, download the generated files. The outputs object returned from WebSocket (or available via the history endpoint) contains output data organized by node ID. Each node’s output may contain images, video, or audio arrays with file metadata. Example outputs structure:
{
  "9": {
    "images": [
      {
        "filename": "ComfyUI_00001_.png",
        "subfolder": "",
        "type": "output"
      }
    ]
  }
}
The node ID ("9" in this example) corresponds to the SaveImage or other output nodes in your workflow. You can find these IDs by opening your workflow JSON file and looking for nodes with class_type like SaveImage, VHS_VideoCombine, etc.
# Download a single output file (follow 302 redirect with -L)
curl -L "$BASE_URL/api/view?filename=output.png&subfolder=&type=output" \
  -H "X-API-Key: $COMFY_CLOUD_API_KEY" \
  -o output.png
The /api/view endpoint returns a 302 redirect to a temporary signed URL. Your HTTP client must follow redirects to download the file.

Complete Example

Here’s a full end-to-end example combining all three steps:
import { readFile, writeFile } from "fs/promises";

const BASE_URL = "https://cloud.comfy.org";
const API_KEY = process.env.COMFY_CLOUD_API_KEY!;

async function main() {
  // 1. Load and modify workflow
  const workflow = JSON.parse(await readFile("workflow_api.json", "utf-8"));
  workflow["3"].inputs.seed = 42;
  workflow["6"].inputs.text = "a beautiful sunset";

  // 2. Submit workflow
  const response = await fetch(`${BASE_URL}/api/prompt`, {
    method: "POST",
    headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ prompt: workflow }),
  });
  const { prompt_id } = await response.json();
  console.log(`Job submitted: ${prompt_id}`);

  // 3. Poll for completion
  while (true) {
    const statusRes = await fetch(`${BASE_URL}/api/job/${prompt_id}/status`, {
      headers: { "X-API-Key": API_KEY },
    });
    const { status } = await statusRes.json();

    if (status === "completed") break;
    if (["failed", "cancelled"].includes(status)) {
      throw new Error(`Job ${status}`);
    }
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }

  // 4. Get outputs via history endpoint
  const historyRes = await fetch(`${BASE_URL}/api/history/${prompt_id}`, {
    headers: { "X-API-Key": API_KEY },
  });
  const history = await historyRes.json();
  const outputs = history[prompt_id].outputs;

  // 5. Download output files
  for (const nodeOutputs of Object.values(outputs)) {
    for (const fileInfo of (nodeOutputs as any).images ?? []) {
      const params = new URLSearchParams({
        filename: fileInfo.filename,
        subfolder: fileInfo.subfolder ?? "",
        type: "output",
      });
      const viewRes = await fetch(`${BASE_URL}/api/view?${params}`, {
        headers: { "X-API-Key": API_KEY },
        redirect: "manual",
      });
      const signedUrl = viewRes.headers.get("location")!;
      const fileRes = await fetch(signedUrl);
      await writeFile(`./${fileInfo.filename}`, Buffer.from(await fileRes.arrayBuffer()));
      console.log(`Downloaded: ${fileInfo.filename}`);
    }
  }
}

main();

Available Endpoints

CategoryDescription
WorkflowsSubmit workflows, check status
JobsMonitor job status and queue
InputsUpload images, masks, and other inputs
OutputsDownload generated content
WebSocketReal-time progress updates
Object InfoAvailable nodes and their definitions

Next Steps

The quick start above covers the basics of submitting workflows and retrieving results. For more advanced use cases, refer to the Cloud API Reference:
  • Uploading Input Files - Upload images, masks, or other user-provided content for workflows that require external inputs
  • Modifying Workflow Inputs - Dynamically change workflow parameters like prompts, seeds, or node settings before submission
  • Using Partner Nodes - Call external AI services (Flux Pro, Ideogram, etc.) that require additional API key configuration
  • Queue Management - Monitor queue status, cancel jobs, or interrupt running executions
  • Error Handling - Handle HTTP errors, execution failures, and understand exception types
Additional resources: