How to Run Flux Text-to-Image with ComfyUI API: A Developer’s Guide

Learn how to deploy Flux text-to-image models with ComfyUI as a scalable API. This guide covers setup, optimization, and best practices for API development and testing—ideal for engineers seeking efficient AI image generation workflows.

Mark Ponomarev

Mark Ponomarev

20 January 2026

How to Run Flux Text-to-Image with ComfyUI API: A Developer’s Guide

Unlocking the full potential of state-of-the-art text-to-image generation requires seamless integration and automation. In this guide, you’ll learn how to combine Flux—an advanced text-to-image model from Black Forest Labs—with ComfyUI’s modular, node-based workflow to create a scalable API for high-quality AI image generation. We’ll cover setup, containerization, API design, performance tuning, and best practices for testing with Apidog, the leading API development platform.

Image


Why Combine Flux and ComfyUI for API-Driven Image Generation?

This combination is perfect for API developers, backend engineers, and technical leads who want scalable, programmatic access to AI-powered image creation.


Essential Tools: Fast API Testing with Apidog

Before diving into the technical setup, let’s talk API testing. Reliable, efficient API testing is crucial for any developer workflow. Apidog stands out as a modern Postman alternative designed for comprehensive API development:

button

Unlike generic API tools, Apidog streamlines the entire API lifecycle, making it ideal for testing and managing endpoints exposed by your Flux ComfyUI API.

button

Setting Up Your Flux + ComfyUI Environment

Prerequisites

Step 1: Build the ComfyUI + Flux Container

Start by creating a container image with all dependencies:

import modal

image = (
    modal.Image.debian_slim(python_version="3.11")
    .apt_install("git")
    .pip_install("fastapi[standard]==0.115.4")
    .pip_install("comfy-cli==1.3.5")
    .run_commands(
        "comfy --skip-prompt install --nvidia --version 0.3.10"
    )
)

This base image prepares your environment with ComfyUI and necessary tools.


Step 2: Add Custom ComfyUI Nodes

Custom nodes extend ComfyUI’s capabilities for image manipulation:

image = (
    image.run_commands(
        "comfy node install was-node-suite-comfyui@1.0.2"
    )
    # Add more custom nodes as needed
)

Step 3: Manage and Download Flux Models

Efficient model storage improves performance and reliability:

def hf_download():
    from huggingface_hub import hf_hub_download
    flux_model = hf_hub_download(
        repo_id="Comfy-Org/flux1-schnell",
        filename="flux1-schnell-fp8.safetensors",
        cache_dir="/cache",
    )
    subprocess.run(
        f"ln -s {flux_model} /root/comfy/ComfyUI/models/checkpoints/flux1-schnell-fp8.safetensors",
        shell=True,
        check=True,
    )

vol = modal.Volume.from_name("hf-hub-cache", create_if_missing=True)

image = (
    image.pip_install("huggingface_hub[hf_transfer]==0.26.2")
    .env({"HF_HUB_ENABLE_HF_TRANSFER": "1"})
    .run_function(
        hf_download,
        volumes={"/cache": vol},
    )
)

Add your workflow template to the container:

image = image.add_local_file(
    Path(__file__).parent / "workflow_api.json", "/root/workflow_api.json"
)

Interactive Workflow Testing in ComfyUI

Before API deployment, test your workflow in the browser:

app = modal.App(name="example-comfyui", image=image)

@app.function(
    allow_concurrent_inputs=10,
    max_containers=1,
    gpu="L40S",
    volumes={"/cache": vol},
)
@modal.web_server(8000, startup_timeout=60)
def ui():
    subprocess.Popen(
        "comfy launch -- --listen 0.0.0.0 --port 8000",
        shell=True
    )

Run modal serve your_file.py and open the UI to fine-tune your workflow.


Implementing the Flux ComfyUI API

Transform your ComfyUI workflow into a robust API:

@app.cls(
    allow_concurrent_inputs=10,
    scaledown_window=300,
    gpu="L40S",
    volumes={"/cache": vol},
)
class ComfyUI:
    @modal.enter()
    def launch_comfy_background(self):
        subprocess.run("comfy launch --background", shell=True, check=True)

    @modal.method()
    def infer(self, workflow_path: str = "/root/workflow_api.json"):
        self.poll_server_health()
        subprocess.run(
            f"comfy run --workflow {workflow_path} --wait --timeout 1200 --verbose",
            shell=True,
            check=True,
        )
        output_dir = "/root/comfy/ComfyUI/output"
        workflow = json.loads(Path(workflow_path).read_text())
        file_prefix = [
            node.get("inputs")
            for node in workflow.values()
            if node.get("class_type") == "SaveImage"
        ][0]["filename_prefix"]
        for f in Path(output_dir).iterdir():
            if f.name.startswith(file_prefix):
                return f.read_bytes()

    @modal.fastapi_endpoint(method="POST")
    def api(self, item: Dict):
        from fastapi import Response
        workflow_data = json.loads(
            (Path(__file__).parent / "workflow_api.json").read_text()
        )
        workflow_data["6"]["inputs"]["text"] = item["prompt"]
        client_id = uuid.uuid4().hex
        workflow_data["9"]["inputs"]["filename_prefix"] = client_id
        new_workflow_file = f"{client_id}.json"
        json.dump(workflow_data, Path(new_workflow_file).open("w"))
        img_bytes = self.infer.local(new_workflow_file)
        return Response(img_bytes, media_type="image/jpeg")

    def poll_server_health(self) -> Dict:
        import socket, urllib
        try:
            req = urllib.request.Request("<http://127.0.0.1:8188/system_stats>")
            urllib.request.urlopen(req, timeout=5)
            print("ComfyUI server is healthy")
        except (socket.timeout, urllib.error.URLError) as e:
            print(f"Server health check failed: {str(e)}")
            modal.experimental.stop_fetching_inputs()
            raise Exception("ComfyUI server is not healthy, stopping container")

[Image]


Testing Your Flux ComfyUI API with Apidog

To validate your new API, use the following example client or Apidog’s intuitive interface for sending and inspecting requests:

import requests
from PIL import Image
import io
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--prompt", default="A surreal landscape with floating islands and ethereal light")
    parser.add_argument("--endpoint", default="<https://your-modal-endpoint.modal.run>")
    args = parser.parse_args()

    response = requests.post(
        f"{args.endpoint}/api",
        json={"prompt": args.prompt}
    )

    if response.status_code == 200:
        image = Image.open(io.BytesIO(response.content))
        image.save("flux_output.jpg")
        print("Image generated and saved as flux_output.jpg")
        try:
            image.show()
        except:
            pass
    else:
        print(f"Error: {response.status_code}, {response.text}")

if __name__ == "__main__":
    main()

For streamlined requests, automated testing, and documentation, Apidog provides a more advanced and collaborative testing environment than alternatives.


Optimizing API Performance for Production

1. Minimize Cold Starts with Memory Snapshotting

Reduce startup time using Modal’s memory snapshotting:

@app.cls(
    allow_concurrent_inputs=10,
    gpu="L40S",
    volumes={"/cache": vol},
    memory_snapshot=modal.MemorySnapshot(
        snapshot_path="/root/comfy-snapshot",
        boot_command="comfy launch --background",
    ),
)
class ComfyUI:
    # Implementation continues...

2. Batch Inference for Higher Throughput

Process multiple prompts in a single request for efficiency:

@modal.method()
def batch_inference(self, prompts: list[str]):
    results = []
    for prompt in prompts:
        client_id = uuid.uuid4().hex
        workflow_data = json.loads(
            (Path(__file__).parent / "workflow_api.json").read_text()
        )
        workflow_data["6"]["inputs"]["text"] = prompt
        workflow_data["9"]["inputs"]["filename_prefix"] = client_id
        new_workflow_file = f"{client_id}.json"
        json.dump(workflow_data, Path(new_workflow_file).open("w"))
        img_bytes = self.infer.local(new_workflow_file)
        results.append(img_bytes)
    return results

Advanced Customization: Workflow Parameters and Model Selection

Accept Additional Parameters

Enable users to customize generation settings:

@modal.fastapi_endpoint(method="POST")
def advanced_api(self, item: Dict):
    from fastapi import Response
    workflow_data = json.loads(
        (Path(__file__).parent / "workflow_api.json").read_text()
    )
    workflow_data["6"]["inputs"]["text"] = item["prompt"]
    if "negative_prompt" in item:
        workflow_data["7"]["inputs"]["text"] = item["negative_prompt"]
    if "cfg_scale" in item:
        workflow_data["3"]["inputs"]["cfg"] = item["cfg_scale"]
    if "steps" in item:
        workflow_data["3"]["inputs"]["steps"] = item["steps"]
    client_id = uuid.uuid4().hex
    workflow_data["9"]["inputs"]["filename_prefix"] = client_id
    new_workflow_file = f"{client_id}.json"
    json.dump(workflow_data, Path(new_workflow_file).open("w"))
    img_bytes = self.infer.local(new_workflow_file)
    return Response(img_bytes, media_type="image/jpeg")

Support Multiple Flux Models

Switch between different Flux checkpoints as needed:

@modal.fastapi_endpoint(method="POST")
def model_selection_api(self, item: Dict):
    from fastapi import Response
    workflow_data = json.loads(
        (Path(__file__).parent / "workflow_api.json").read_text()
    )
    workflow_data["6"]["inputs"]["text"] = item["prompt"]
    if "model" in item:
        if item["model"] == "flux-schnell":
            workflow_data["2"]["inputs"]["ckpt_name"] = "flux1-schnell-fp8.safetensors"
        elif item["model"] == "flux-turbo":
            workflow_data["2"]["inputs"]["ckpt_name"] = "flux1-turbo-fp8.safetensors"
    client_id = uuid.uuid4().hex
    workflow_data["9"]["inputs"]["filename_prefix"] = client_id
    new_workflow_file = f"{client_id}.json"
    json.dump(workflow_data, Path(new_workflow_file).open("w"))
    img_bytes = self.infer.local(new_workflow_file)
    return Response(img_bytes, media_type="image/jpeg")

Monitoring and Logging

Ensure reliability and observability in production:

@app.cls(
    # Other parameters
    monitor_agent=modal.MonitorAgent(),
)
class ComfyUI:
    def log_request(self, prompt, model, processing_time):
        print(f"Generated image for prompt: '{prompt}' using {model} model in {processing_time:.2f}s")

Conclusion: Streamline AI Image Generation with API-First Workflows

With this comprehensive setup, you can deploy a scalable, customizable Flux ComfyUI API—ready for integration into any application. Combining ComfyUI’s workflow management with Flux’s image quality and Modal’s serverless infrastructure, you gain:

For every step of API design, testing, and maintenance, Apidog streamlines your workflow—making API-driven AI image generation robust and collaborative.

button

Explore more

How to Use Claude Code for CI/CD Workflows

How to Use Claude Code for CI/CD Workflows

Technical guide to integrating Claude Code into CI/CD pipelines. Covers container setup, GitHub Actions/GitLab CI integration, skill development, and practical workflows for DevOps automation.

21 January 2026

How to Use Claude Code Skills for API Request/Networking (data-fetching)

How to Use Claude Code Skills for API Request/Networking (data-fetching)

Technical guide to using Claude Code skills for API networking. Covers setup, core request patterns, advanced scenarios, and practical examples for building AI-driven data-fetching workflows.

21 January 2026

How to Use Claude Code Skills for Building UI

How to Use Claude Code Skills for Building UI

Technical guide to using Claude Code skills for UI development. Covers setup, core tools, advanced patterns, and debugging for building production-ready React/Vue/Svelte components.

21 January 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs