Skip to documentation content
Developer Docs

REST API

Server-side7 operations

Nordy API

Run ComfyUI workflows and retrieve their results with the Nordy REST API.

Base URL
https://api.nordy.ai
AuthenticationGet an API key
api-key: $NORDY_API_KEY
On this page

Start here

Get an API key

You need an active Pro subscription. Open Manage API keys to create a key. You can have up to three active keys; each complete key is shown only once.

Send the key in the api-key header. Store it in a server environment variable and keep it out of browser code, mobile apps, and public repositories.

bash
export NORDY_API_KEY="your_api_key"
export NORDY_BASE_URL="https://api.nordy.ai"

Job creation limit

Job creation is subject to a daily limit.

Quickstart

These examples use Bash, curl, and jq. Run the steps in the same terminal and directory so they can reuse the environment variables and saved files.

1. Prepare your workflow. Test it in Nordy ComfyUI with a Save node for the outputs you need. Save the Export Workflow (API Format) or Export (API) result as prompt.json; editor-format JSON cannot be used. You can start with an example workflow.

2. Create a Job. Run this command once. It wraps your prompt in the request body and saves the response to job.json.

bash
jq '{prompt: .}' prompt.json > request.json &&
curl --fail-with-body --silent --show-error --max-time 30 \
  -X POST "$NORDY_BASE_URL/v1/api/job/comfy-prompt" \
  -H "api-key: $NORDY_API_KEY" \
  -H "content-type: application/json" \
  --data-binary @request.json --output job.json &&
jq '{id, status}' job.json

A 202 Accepted response means the Job was accepted. Keep its id; completion is checked separately.

3. Check the result. Read the saved Job ID and fetch its current status.

bash
JOB_ID=$(jq -er '.id' job.json) &&
curl --fail-with-body --silent --show-error --max-time 30 \
  "$NORDY_BASE_URL/v1/api/job/$JOB_ID" \
  -H "api-key: $NORDY_API_KEY" --output job-status.json &&
jq '{status, outputs, error}' job-status.json
StatusNext step
queued / processingWait at least 5 seconds, then repeat step 3 only.
succeededDownload the files listed in outputs.
failed / canceledCheck error.code and error.message. See Failed Jobs.

For automatic status checks, use the polling script. If creation times out, check Retries and timeouts before submitting again.

4. Download the output

After the Job succeeds, this command saves the first result as output.bin. Use an extension appropriate for the file, and download each URL if the Job has multiple outputs. Do not send the API key to download URLs.

bash
OUTPUT_URL=$(jq -er \
  'select(.status == "succeeded") | .outputs[0].url // empty' job-status.json) &&
curl --fail --location --show-error \
  --output output.bin --url "$OUTPUT_URL"

Outputs expire 7 days after they are created. Save each file before its expiresAt timestamp. Once all outputs expire, the Job remains available with outputs: [].

Example workflows

Choose an example, save its JSON as prompt.json, and follow Quickstart to create and check the Job.

GoalExample
Generate an image from textDreamShaper
Try a workflow without a checkpointLoad and save an image

Generate an image with DreamShaper

Check the available checkpoint names first. Use dreamshaper_8.safetensors if listed, or update 4.inputs.ckpt_name to an available compatible checkpoint. An empty object means the node is unavailable.

bash
curl --fail-with-body --silent --show-error \
  "$NORDY_BASE_URL/v1/api/comfy/object-info/CheckpointLoaderSimple" \
  -H "api-key: $NORDY_API_KEY" --output object-info.json &&
jq -r '.CheckpointLoaderSimple.input.required.ckpt_name[0][]' object-info.json

This example needs no input image.

Full codeJSON
JSON
{
  "3": {
    "inputs": {
      "seed": 102298450217384,
      "steps": 20,
      "cfg": 8,
      "sampler_name": "euler",
      "scheduler": "normal",
      "denoise": 1,
      "model": ["4", 0],
      "positive": ["6", 0],
      "negative": ["7", 0],
      "latent_image": ["5", 0]
    },
    "class_type": "KSampler",
    "_meta": {
      "title": "KSampler"
    }
  },
  "4": {
    "inputs": {
      "ckpt_name": "dreamshaper_8.safetensors"
    },
    "class_type": "CheckpointLoaderSimple",
    "_meta": {
      "title": "Load Checkpoint"
    }
  },
  "5": {
    "inputs": {
      "width": 256,
      "height": 256,
      "batch_size": 1
    },
    "class_type": "EmptyLatentImage",
    "_meta": {
      "title": "Empty Latent Image"
    }
  },
  "6": {
    "inputs": {
      "text": "beautiful scenery nature glass bottle landscape, purple galaxy bottle",
      "clip": ["4", 1]
    },
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "CLIP Text Encode (Positive)"
    }
  },
  "7": {
    "inputs": {
      "text": "text, watermark",
      "clip": ["4", 1]
    },
    "class_type": "CLIPTextEncode",
    "_meta": {
      "title": "CLIP Text Encode (Negative)"
    }
  },
  "8": {
    "inputs": {
      "samples": ["3", 0],
      "vae": ["4", 2]
    },
    "class_type": "VAEDecode",
    "_meta": {
      "title": "VAE Decode"
    }
  },
  "9": {
    "inputs": {
      "filename_prefix": "dreamshaper-api-example",
      "images": ["8", 0]
    },
    "class_type": "SaveImage",
    "_meta": {
      "title": "Save Image"
    }
  }
}
To changeEdit
Positive and negative prompts6.inputs.text and 7.inputs.text
Seed and sampling settings3.inputs
Image size and batch size5.inputs
Checkpoint4.inputs.ckpt_name

Keep node connections unchanged. A listed node or model may still be unavailable when the Job runs.

Load and save an image from a URL

This example uses LoadImage → SaveImage without a checkpoint or custom node. Replace the image URL with your own.

Full codeJSON
JSON
{
  "11": {
    "inputs": {
      "image": "https://your-public-host.example/input.png"
    },
    "class_type": "LoadImage",
    "_meta": {
      "title": "Load Image"
    }
  },
  "12": {
    "inputs": {
      "filename_prefix": "nordy-api-example",
      "images": [
        "11",
        0
      ]
    },
    "class_type": "SaveImage",
    "_meta": {
      "title": "Save Image"
    }
  }
}

Use an HTTPS URL accessible without login cookies or custom headers, and keep the connection ["11", 0] unchanged. A presigned URL must stay valid until the Job finishes and allow both HEAD and GET; a GET-only signature can be rejected.

The image is decoded and saved again, so encoding, metadata, and transparency may change.

API reference

Select an API to view its parameters, response fields, and examples. Each reference page also links to API TEST with that API selected.

OperationMethod and pathSuccess
Create a JobPOST /v1/api/job/comfy-prompt202
List JobsGET /v1/api/job/list200
Get Job status and resultsGET /v1/api/job/{id}200
Cancel JobsPOST /v1/api/job/cancel200
Cancel all JobsPOST /v1/api/job/cancel-all200
Get all node definitionsGET /v1/api/comfy/object-info200
Get one node definitionGET /v1/api/comfy/object-info/{className}200

Troubleshooting

See Responses and errors for all HTTP statuses, error codes, Job statuses, and cancellation results.

HTTP request errors

Nordy errors include a code, a message, and a requestId you can share with support.

Full codeJSON
JSON
{
  "statusCode": 400,
  "code": "INVALID_INPUT",
  "message": "The request is invalid.",
  "requestId": "request-id",
  "details": {
    "field": "prompt"
  }
}
Status and codeWhat to check
400 INVALID_JSON / INVALID_INPUTValidate the JSON, use an API-format prompt, and wrap it in prompt once.
400 MULTIPLE_AUTH_CREDENTIALSSend the API key without login cookies.
401 AUTHENTICATION_REQUIREDCheck the api-key header and whether the key was deleted.
402 INSUFFICIENT_CREDITCheck your credit balance.
403 PRO_SUBSCRIPTION_REQUIREDCheck that your Pro subscription is active.
403 USER_SUSPENDEDContact support about your account status.
404 JOB_NOT_FOUNDCheck the Job ID and API key account. Only Jobs created through this API are available.
413 REQUEST_BODY_TOO_LARGEKeep the JSON request within 50 MiB.
415 UNSUPPORTED_MEDIA_TYPESend Content-Type: application/json.
429 DAILY_JOB_LIMIT_EXCEEDEDTry again after the creation limit resets.
5xxFor lookup requests, wait before retrying. For creation requests, follow the guidance below.

The API reference lists the responses for each endpoint. Preserve unknown error codes as well as known ones when handling errors.

Failed Jobs

HTTP 200 means the lookup worked; the Job itself may still have failed. Check status and error together.

Job errorNext step
INVALID_INPUTCheck node inputs and model names. error.details.node_errors may identify the affected node.
INSUFFICIENT_CREDITCheck your credit balance before creating another Job.
EXECUTION_TIMEOUT / WORKER_UNAVAILABLE / EXECUTION_FAILEDReview the error message. Contact support if the issue persists.
JOB_CANCELEDThe Job is canceled. Other errors use failed.

Validation errors may include submitted values and model choices. Share only the details needed for support and avoid publishing the complete error object.

Full codeJSON
JSON
{
  "id": "68abcdef0123456789abcdef",
  "user_metadata": {},
  "status": "failed",
  "createdAt": "2026-08-26T00:00:00.000Z",
  "startedAt": "2026-08-26T00:00:02.000Z",
  "completedAt": "2026-08-26T00:00:04.000Z",
  "outputs": null,
  "error": {
    "code": "INVALID_INPUT",
    "message": "The job input is invalid.",
    "details": {
      "error": {
        "type": "prompt_outputs_failed_validation",
        "message": "Prompt outputs failed validation"
      },
      "node_errors": {
        "4": {
          "class_type": "CheckpointLoaderSimple",
          "errors": [
            {
              "type": "value_not_in_list",
              "message": "Value not in list"
            }
          ]
        }
      }
    }
  }
}

Retries and timeouts

  • Do not automatically repeat a creation request after a timeout or lost response. A Job may already exist. Use the saved ID or check recent Jobs before deciding whether to submit again.
  • You can retry a failed GET lookup with the same ID after a delay. For queued or processing Jobs, follow Retry-After. Resolve authentication and input errors before retrying.

Request limits

See Input limits for prompt and metadata formats, sizes, and node counts.

Advanced usage

Automatic polling

Run this script with the Quickstart environment and job.json. It follows Retry-After when valid, otherwise waits 5 seconds, and saves the latest response to job-status.json.

The script stops after 2 hours; this is a client waiting limit and does not cancel the Job. It prints output URLs on success and an error on failure. Download successful results using step 4.

Full codebash
bash
if ! JOB_ID=$(jq -er '.id' job.json); then
  printf 'Could not read the Job ID from job.json.\n' >&2
  exit 1
fi

POLL_TIMEOUT_SECONDS=7200
POLL_DEADLINE=$((SECONDS + POLL_TIMEOUT_SECONDS))

while true; do
  REMAINING_SECONDS=$((POLL_DEADLINE - SECONDS))
  if (( REMAINING_SECONDS <= 0 )); then
    printf 'Timed out waiting for Job %s.\n' "$JOB_ID" >&2
    exit 1
  fi

  REQUEST_TIMEOUT_SECONDS=30
  if (( REMAINING_SECONDS < REQUEST_TIMEOUT_SECONDS )); then
    REQUEST_TIMEOUT_SECONDS=$REMAINING_SECONDS
  fi

  if ! HTTP_STATUS=$(curl --fail-with-body --silent --show-error \
    --max-time "$REQUEST_TIMEOUT_SECONDS" \
    "$NORDY_BASE_URL/v1/api/job/$JOB_ID" \
    -H "api-key: $NORDY_API_KEY" \
    --dump-header job-headers.txt \
    --output job-status.json \
    --write-out '%{http_code}'); then
    if [ -s job-status.json ]; then cat job-status.json >&2; fi
    exit 1
  fi

  case "$HTTP_STATUS" in
    2??) ;;
    *)
      printf 'Unexpected HTTP status: %s\n' "$HTTP_STATUS" >&2
      cat job-status.json >&2
      exit 1
      ;;
  esac

  if (( SECONDS >= POLL_DEADLINE )); then
    printf 'Timed out waiting for Job %s.\n' "$JOB_ID" >&2
    exit 1
  fi

  if ! STATUS=$(jq -er '.status' job-status.json); then
    printf 'The response did not include a valid Job status.\n' >&2
    exit 1
  fi
  printf 'Job status: %s\n' "$STATUS"

  case "$STATUS" in
    succeeded|failed|canceled) break ;;
    queued|processing) ;;
    *)
      printf 'Unknown Job status: %s\n' "$STATUS" >&2
      exit 1
      ;;
  esac

  if (( SECONDS >= POLL_DEADLINE )); then
    printf 'Timed out waiting for Job %s.\n' "$JOB_ID" >&2
    exit 1
  fi

  RETRY_AFTER=$(
    awk -F ': *' '
      tolower($1) == "retry-after" {
        gsub("\\r", "", $2)
        print $2
        exit
      }
    ' job-headers.txt
  )
  case "$RETRY_AFTER" in
    ''|*[!0-9]*) RETRY_AFTER=5 ;;
  esac

  REMAINING_SECONDS=$((POLL_DEADLINE - SECONDS))
  if (( RETRY_AFTER > REMAINING_SECONDS )); then
    RETRY_AFTER=$REMAINING_SECONDS
  fi
  sleep "$RETRY_AFTER"
done

if [ "$STATUS" = "succeeded" ]; then
  jq -r '.outputs[] | "\(.kind)\t\(.url)"' job-status.json
else
  jq '{code: .error.code, message: .error.message}' job-status.json
  exit 1
fi

List more Jobs

Fetch the first page:

bash
curl --fail-with-body --silent --show-error --max-time 30 --get \
  "$NORDY_BASE_URL/v1/api/job/list" \
  -H "api-key: $NORDY_API_KEY" \
  --data-urlencode 'limit=20' --output jobs.json &&
jq . jobs.json

This command passes next_cursor unchanged to fetch the next page and update jobs.json. It sends no request when the cursor is null:

bash
NEXT_CURSOR=$(jq -er '.next_cursor // empty' jobs.json) &&
curl --fail-with-body --silent --show-error --max-time 30 --get \
  "$NORDY_BASE_URL/v1/api/job/list" \
  -H "api-key: $NORDY_API_KEY" \
  --data-urlencode 'limit=20' \
  --data-urlencode "cursor=$NEXT_CURSOR" --output jobs.json &&
jq . jobs.json

Cancel pending Jobs

Only Pending Jobs, before execution preparation begins, can be canceled. Choose Cancel Jobs to provide an array of Job IDs, or Cancel all Jobs for your account’s entire pending queue. Each reference includes the request example and response details.

Add your own metadata

Use user_metadata to associate a Job with a record in your application. For example:

JSON
{"order_id": "example-order-001"}

Metadata is returned with the Job, but does not affect workflow inputs or prevent duplicate creation. See Input limits for supported values.

Terms and abbreviations

TermMeaning
WorkflowConnected ComfyUI nodes that generate or process content.
API-format promptJSON that defines the nodes and connections to execute.
NodeOne workflow step, such as loading a model or saving an image.
Job / Job IDOne workflow execution and its lookup ID.
outputsGenerated files with download URLs and expiration times. One Job can return multiple files.

Next steps