-
Notifications
You must be signed in to change notification settings - Fork 172
docs: recommend Jobs API instead of history/queue endpoints #742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b5e936c
3b81a9d
ca7fb41
6960c75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -183,7 +183,7 @@ | |
| ### Upload Mask | ||
|
|
||
| <Note> | ||
| The `subfolder` parameter is accepted for API compatibility but ignored in cloud storage. All files are stored in a flat, content-addressed namespace. | ||
| </Note> | ||
|
|
||
| <CodeGroup> | ||
|
|
@@ -409,8 +409,8 @@ | |
| ```typescript TypeScript | ||
| function setWorkflowInput( | ||
| workflow: Record<string, any>, | ||
| nodeId: string, | ||
| inputName: string, | ||
| value: any | ||
| ): Record<string, any> { | ||
| if (workflow[nodeId]) { | ||
|
|
@@ -450,6 +450,196 @@ | |
|
|
||
| --- | ||
|
|
||
| ## Jobs API | ||
|
|
||
| The Jobs API provides efficient endpoints for listing and retrieving job details. Use these endpoints instead of the legacy `/history` and `/queue` endpoints for better performance and richer filtering options. | ||
|
|
||
| ### List Jobs | ||
|
|
||
| Retrieve a paginated list of jobs with optional filtering by status, workflow ID, or output type. | ||
|
|
||
| <CodeGroup> | ||
| ```bash curl | ||
| # List all jobs (most recent first) | ||
| curl -X GET "$BASE_URL/api/jobs" \ | ||
| -H "X-API-Key: $COMFY_CLOUD_API_KEY" | ||
|
|
||
| # Filter by status | ||
| curl -X GET "$BASE_URL/api/jobs?status=completed" \ | ||
| -H "X-API-Key: $COMFY_CLOUD_API_KEY" | ||
|
|
||
| # Filter by output type and sort by execution time | ||
| curl -X GET "$BASE_URL/api/jobs?output_type=image&sort_by=execution_time&sort_order=desc" \ | ||
| -H "X-API-Key: $COMFY_CLOUD_API_KEY" | ||
| ``` | ||
|
|
||
| ```typescript TypeScript | ||
| interface JobEntry { | ||
| id: string; | ||
| status: "pending" | "in_progress" | "completed" | "failed" | "cancelled"; | ||
| create_time: number; | ||
| preview_output?: Record<string, any>; | ||
| outputs_count?: number; | ||
| workflow_id?: string; | ||
| execution_start_time?: number; | ||
| execution_end_time?: number; | ||
| } | ||
|
|
||
| interface JobsListResponse { | ||
| jobs: JobEntry[]; | ||
| pagination: { | ||
| offset: number; | ||
| limit: number; | ||
| total: number; | ||
| has_more: boolean; | ||
| }; | ||
| } | ||
|
|
||
| async function listJobs(options: { | ||
| status?: string; | ||
| output_type?: "image" | "video" | "audio"; | ||
| sort_by?: "create_time" | "execution_time"; | ||
| sort_order?: "asc" | "desc"; | ||
| offset?: number; | ||
| limit?: number; | ||
| } = {}): Promise<JobsListResponse> { | ||
| const params = new URLSearchParams(); | ||
| if (options.status) params.set("status", options.status); | ||
| if (options.output_type) params.set("output_type", options.output_type); | ||
| if (options.sort_by) params.set("sort_by", options.sort_by); | ||
| if (options.sort_order) params.set("sort_order", options.sort_order); | ||
| if (options.offset !== undefined) params.set("offset", String(options.offset)); | ||
| if (options.limit !== undefined) params.set("limit", String(options.limit)); | ||
|
|
||
| const response = await fetch(`${BASE_URL}/api/jobs?${params}`, { | ||
| headers: getHeaders(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The TypeScript example on this line calls A developer copy-pasting these snippets gets a Same issue at line 600 ( |
||
| }); | ||
| if (!response.ok) throw new Error(`HTTP ${response.status}`); | ||
| return response.json(); | ||
| } | ||
|
|
||
| // List recent completed jobs | ||
| const { jobs, pagination } = await listJobs({ status: "completed", limit: 10 }); | ||
| console.log(`Found ${pagination.total} completed jobs`); | ||
| ``` | ||
|
|
||
| ```python Python | ||
| def list_jobs( | ||
| status: str = None, | ||
| output_type: str = None, | ||
| sort_by: str = "create_time", | ||
| sort_order: str = "desc", | ||
| offset: int = 0, | ||
| limit: int = 100 | ||
| ) -> dict: | ||
| """List jobs with optional filtering. | ||
|
|
||
| Args: | ||
| status: Filter by status (pending, in_progress, completed, failed, cancelled) | ||
| output_type: Filter by output type (image, video, audio) | ||
| sort_by: Sort field (create_time or execution_time) | ||
| sort_order: Sort direction (asc or desc) | ||
| offset: Pagination offset | ||
| limit: Max items per page (1-1000) | ||
|
|
||
| Returns: | ||
| Dict with 'jobs' array and 'pagination' info | ||
| """ | ||
| params = { | ||
| "sort_by": sort_by, | ||
| "sort_order": sort_order, | ||
| "offset": offset, | ||
| "limit": limit | ||
| } | ||
| if status: | ||
| params["status"] = status | ||
| if output_type: | ||
| params["output_type"] = output_type | ||
|
|
||
| response = requests.get( | ||
| f"{BASE_URL}/api/jobs", | ||
| headers=get_headers(), | ||
| params=params | ||
| ) | ||
| response.raise_for_status() | ||
| return response.json() | ||
|
|
||
| # List recent completed jobs | ||
| result = list_jobs(status="completed", limit=10) | ||
| print(f"Found {result['pagination']['total']} completed jobs") | ||
| ``` | ||
| </CodeGroup> | ||
|
|
||
| ### Get Job Details | ||
|
|
||
| Retrieve complete details for a specific job including the workflow and full outputs. | ||
|
|
||
| <CodeGroup> | ||
| ```bash curl | ||
| curl -X GET "$BASE_URL/api/jobs/{job_id}" \ | ||
| -H "X-API-Key: $COMFY_CLOUD_API_KEY" | ||
| ``` | ||
|
|
||
| ```typescript TypeScript | ||
| interface JobDetailResponse { | ||
| id: string; | ||
| status: "pending" | "in_progress" | "completed" | "failed" | "cancelled"; | ||
| workflow?: Record<string, any>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the API returns a workflow, sensitive credentials inside A developer reading these docs and round-tripping a returned workflow back into At minimum, add a short callout above this interface: "Returned workflows have |
||
| outputs?: Record<string, any>; | ||
| preview_output?: Record<string, any>; | ||
| outputs_count?: number; | ||
| create_time: number; | ||
| update_time: number; | ||
| workflow_id?: string; | ||
| execution_status?: Record<string, any>; | ||
| execution_meta?: Record<string, any>; | ||
| } | ||
|
|
||
| async function getJobDetails(jobId: string): Promise<JobDetailResponse> { | ||
| const response = await fetch(`${BASE_URL}/api/jobs/${jobId}`, { | ||
| headers: getHeaders(), | ||
| }); | ||
| if (!response.ok) throw new Error(`HTTP ${response.status}`); | ||
| return response.json(); | ||
| } | ||
|
|
||
| // Get full job details including outputs | ||
| const job = await getJobDetails(promptId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This page never says they're the same value. A developer who got their prompt id from
(The JSON example response above also uses |
||
| if (job.status === "completed" && job.outputs) { | ||
| console.log(`Job has ${job.outputs_count} outputs`); | ||
| } | ||
| ``` | ||
|
|
||
| ```python Python | ||
| def get_job_details(job_id: str) -> dict: | ||
| """Get complete job details including workflow and outputs. | ||
|
|
||
| Args: | ||
| job_id: The job/prompt ID | ||
|
|
||
| Returns: | ||
| Full job details including workflow and outputs | ||
| """ | ||
| response = requests.get( | ||
| f"{BASE_URL}/api/jobs/{job_id}", | ||
| headers=get_headers() | ||
| ) | ||
| response.raise_for_status() | ||
| return response.json() | ||
|
|
||
| # Get full job details including outputs | ||
| job = get_job_details(prompt_id) | ||
| if job["status"] == "completed" and job.get("outputs"): | ||
| print(f"Job has {job.get('outputs_count', 0)} outputs") | ||
| ``` | ||
| </CodeGroup> | ||
|
|
||
| <Note> | ||
| **Legacy Endpoints:** The `/api/history`, `/api/history_v2`, and `/api/queue` endpoints are maintained for compatibility with local ComfyUI but the Jobs API is recommended for new integrations. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The compatibility justification is factually wrong, and undersells the actual deprecation status. Two issues:
Suggested rewrite:
|
||
| </Note> | ||
|
|
||
| --- | ||
|
|
||
| ## Checking Job Status | ||
|
|
||
| Poll for job completion. | ||
|
|
@@ -511,7 +701,7 @@ | |
| | Offset | Size | Field | Description | | ||
| |--------|------|-------|-------------| | ||
| | 0 | 4 bytes | `type` | `0x00000003` | | ||
| | 4 | 4 bytes | `node_id_len` | Length of node_id string | | ||
| | 8 | N bytes | `node_id` | UTF-8 encoded node ID | | ||
| | 8+N | variable | `text` | UTF-8 encoded progress text | | ||
| </Tab> | ||
|
|
@@ -575,7 +765,7 @@ | |
| } | ||
|
|
||
| async function waitForCompletion( | ||
| promptId: string, | ||
| timeout: number = 300000 | ||
| ): Promise<Record<string, any>> { | ||
| const wsUrl = `wss://cloud.comfy.org/ws?clientId=${crypto.randomUUID()}&token=${API_KEY}`; | ||
|
|
@@ -619,7 +809,7 @@ | |
|
|
||
| async function downloadOutputs( | ||
| outputs: Record<string, any>, | ||
| outputDir: string | ||
| ): Promise<void> { | ||
| for (const nodeOutputs of Object.values(outputs)) { | ||
| for (const key of ["images", "video", "audio"]) { | ||
|
|
@@ -635,9 +825,9 @@ | |
| redirect: "manual", | ||
| }); | ||
| if (response.status !== 302) throw new Error(`HTTP ${response.status}`); | ||
| const signedUrl = response.headers.get("location")!; | ||
| // Fetch from signed URL without auth headers | ||
| const fileResponse = await fetch(signedUrl); | ||
| if (!fileResponse.ok) throw new Error(`HTTP ${fileResponse.status}`); | ||
|
|
||
| const path = `${outputDir}/${fileInfo.filename}`; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,15 +30,17 @@ | |
| | `/upload/mask` | post | upload a mask | | ||
| | `/view` | get | view an image. Lots of options, see `@routes.get("/view")` in `server.py` | | ||
| | `/view_metadata`/{folder_name} | get | retrieve metadata for a model | | ||
| | `/system_stats` | get | retrieve information about the system (python version, devices, vram etc) | | ||
| | `/prompt` | get | retrieve current queue status and execution information | | ||
| | `/prompt` | post | submit a prompt to the queue | | ||
| | `/object_info` | get | retrieve details of all node types | | ||
| | `/object_info/{node_class}` | get | retrieve details of one node type | | ||
| | `/history` | get | retrieve the queue history | | ||
| | `/history/{prompt_id}` | get | retrieve the queue history for a specific prompt | | ||
| | `/jobs` | get | list jobs with filtering, sorting, and pagination (recommended) | | ||
| | `/jobs/{job_id}` | get | get full details for a specific job (recommended) | | ||
| | `/history` | get | retrieve the queue history (legacy) | | ||
| | `/history/{prompt_id}` | get | retrieve the queue history for a specific prompt (legacy) | | ||
| | `/history` | post | clear history or delete history item | | ||
| | `/queue` | get | retrieve the current state of the execution queue | | ||
| | `/queue` | get | retrieve the current state of the execution queue (legacy) | | ||
| | `/queue` | post | manage queue operations (clear pending/running) | | ||
| | `/interrupt` | post | stop the current workflow execution | | ||
| | `/free` | post | free memory by unloading specified models | | ||
|
|
@@ -51,6 +53,71 @@ | |
| | `/users` | get | get user information | | ||
| | `/users` | post | create a new user (multi-user mode only) | | ||
|
|
||
| ### Jobs API | ||
|
|
||
| The Jobs API (`/jobs`) provides a unified interface for listing and retrieving job information with filtering, sorting, and pagination. It is the recommended way to query job status and history. | ||
|
|
||
| #### List Jobs | ||
|
|
||
| `GET /jobs` returns a paginated list of jobs with optional filtering: | ||
|
|
||
| | Parameter | Type | Description | | ||
| |-----------|------|-------------| | ||
| | `status` | string | Filter by status (comma-separated): `pending`, `in_progress`, `completed`, `failed`, `cancelled` | | ||
| | `workflow_id` | string | Filter by workflow ID | | ||
| | `sort_by` | string | Sort field: `created_at` (default), `execution_duration` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The live cloud API accepts A reader following this table will send |
||
| | `sort_order` | string | Sort direction: `asc`, `desc` (default) | | ||
| | `limit` | integer | Maximum items to return | | ||
| | `offset` | integer | Items to skip (default: 0) | | ||
|
|
||
| **Response:** | ||
| ```json | ||
| { | ||
| "jobs": [ | ||
| { | ||
| "id": "prompt-uuid", | ||
| "status": "completed", | ||
| "create_time": 1706540000, | ||
| "workflow_id": "workflow-uuid", | ||
| "outputs_count": 2, | ||
| "preview_output": {"filename": "output.png", "type": "output"}, | ||
| "execution_start_time": 1706540001000, | ||
|
Comment on lines
+80
to
+84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Timestamp units mismatched in the same payload, and The API actually returns Clients parsing this as an example will either:
Fix the example to use 13-digit ms throughout: |
||
| "execution_end_time": 1706540005000 | ||
| } | ||
| ], | ||
| "pagination": { | ||
| "offset": 0, | ||
| "limit": 100, | ||
| "total": 42, | ||
| "has_more": false | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Get Job Details | ||
|
|
||
| `GET /jobs/{job_id}` returns full details for a specific job including outputs and workflow: | ||
|
|
||
| ```json | ||
| { | ||
| "id": "prompt-uuid", | ||
| "status": "completed", | ||
| "create_time": 1706540000, | ||
| "outputs": { | ||
| "9": {"images": [{"filename": "output.png", "type": "output"}]} | ||
| }, | ||
| "workflow": { | ||
| "prompt": {...}, | ||
| "extra_data": {...} | ||
| }, | ||
| "execution_status": {...} | ||
| } | ||
| ``` | ||
|
|
||
| <Note> | ||
| The Jobs API consolidates data from the queue and history into a single unified format. Use `/jobs` instead of separately querying `/queue` and `/history` endpoints. | ||
| </Note> | ||
|
|
||
| ### WebSocket Communication | ||
|
|
||
| The `/ws` endpoint provides real-time bidirectional communication between the client and server. This is used for: | ||
|
|
@@ -87,7 +154,7 @@ | |
|
|
||
| <Tip>Unless you know what you are doing, don't try to define `my_function` within a class. | ||
| The `@routes.post` decorator does a lot of work! Instead, define the function as above | ||
| and then call a classmethod.</Tip> | ||
|
|
||
| <Tip>You can also define a `@routes.get` if you aren't changing anything.</Tip> | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output_typeenum is missing"3d".The live API accepts a fourth value,
"3d", in addition toimage | video | audio. The TypeScript signature here drops it, so anyone copy-pasting this type into their own SDK will get a compile error the moment they try to filter for 3D outputs.The Python docstring on line 539 has the same issue.