REST API for stable-diffusion.cpp image and video generation.
Base URL: http://<host>:<port>
Canonical field reference:
API_REFERENCE.mdis auto-generated from/openapi.jsonand is always in sync with the running server. Use it as the source of truth for exact field names, types, and defaults. This document explains workflows and gives examples. Default:http://localhost:8080
Content-Type: All requests and responses use application/json
- Authentication
- Health Check
- Memory
- Model Management
- Image Generation
- Upscaler Management
- Queue Management
- Recycle Bin
- Preview Settings
- Settings
- Output Settings
- File Browser & Output Serving
- Server Options
- Model Conversion
- Assistant Integration
- Model Architecture
- Model Downloads
- WebSocket Support
- Error Responses
- Data Types
When the server is built with auth enabled and a username/password is configured (auth.username + auth.password in config.json, or via SDCPP_AUTH_USERNAME / SDCPP_AUTH_PASSWORD env vars), most endpoints require authentication. The two transports are:
- Bearer token — programmatic clients (REST, MCP). Obtain a token by POSTing credentials to
/auth/loginand pass it asAuthorization: Bearer <token>on subsequent requests. - HttpOnly cookie — the WebUI. The same
/auth/loginendpoint sets aSet-Cookie: sdcpp_session=<token>; HttpOnly; SameSite=Strictheader when called from a browser; the cookie is automatically attached to all subsequent requests and to the WebSocket handshake (browsers can't set custom headers on WS).
Endpoints exempt from auth: /login (the static HTML page), /login.css, /auth/login, /health, /openapi.json, /docs/, /ui/ (the SPA shell), and /webdav/ (which uses HTTP Basic).
Public output access (auth.allow_public_outputs) — when this option is true (default), /output/<path> and /thumb/<path> also bypass auth so generated images can be embedded in third-party clients via direct URL (Discord/Slack unfurls, an <img src="https://host/output/.../output_0.png"> in another site, scripts that don't carry a token). Set auth.allow_public_outputs: false in config.json to require the same bearer/cookie auth on those paths as everywhere else. Has no effect when auth.enabled: false — there's nothing to bypass.
{
"auth": {
"enabled": true,
"allow_public_outputs": true
}
}Trusted reverse proxies (server.trusted_proxies) — when the server is fronted by a reverse proxy that terminates TLS (nginx, Caddy, Traefik, kube-ingress), the proxy injects X-Forwarded-Proto and X-Forwarded-Host. The server only honors those headers when the connecting peer's IP is in this allowlist; otherwise the literal Host header (with http://) is used. Empty list = never trust forwarded headers. The list affects URL construction in responses (e.g. output_urls[]) and is the only way to get https:// URLs returned in payloads when behind a TLS-terminating proxy. Entries may be exact IPs ("127.0.0.1", "::1") or IPv4 CIDRs ("10.0.0.0/8").
{
"server": {
"trusted_proxies": ["127.0.0.1", "10.0.0.0/8"]
}
}Exchange username + password for a session token (REST) or set a session cookie (browser).
Request Body:
{
"username": "fszontagh",
"password": "your-password-here"
}Success Response (200):
{
"token": "9f3a1e2d4b8c6f0a..."
}The response also sets an HttpOnly; SameSite=Strict cookie on browser clients. CLI/script clients should capture token and use it on subsequent requests:
curl -H "Authorization: Bearer 9f3a1e2d4b8c6f0a..." http://localhost:8080/queueError Response (401):
{
"error": "Invalid credentials"
}Failed login attempts are delayed by 200 ms server-side to slow down brute force. The credential comparison is constant-time.
Invalidate the current session token (and clear the cookie if the call is browser-driven).
Request Body: None required (the token is taken from the request — Bearer header or cookie).
Success Response (200):
{
"success": true
}Check server status, loaded model, and all loaded components.
Response:
{
"status": "ok",
"version": "1.2.3",
"git_commit": "abc1234",
"model_loaded": true,
"model_loading": false,
"loading_model_name": null,
"loading_step": 0,
"loading_total_steps": 0,
"last_error": null,
"model_name": "z_image_turbo-Q8_0.gguf",
"model_type": "diffusion",
"model_architecture": "Z-Image",
"loaded_components": {
"vae": "ae_q8_0.gguf",
"clip_l": null,
"clip_g": null,
"t5xxl": null,
"controlnet": null,
"llm": "qwen_3_4b.Q8_0.gguf",
"llm_vision": null
},
"upscaler_loaded": false,
"upscaler_name": null,
"ws_enabled": true,
"load_options": {
"n_threads": -1,
"flash_attn": true,
"diffusion_flash_attn": true,
"enable_mmap": true,
"vae_conv_direct": true,
"diffusion_conv_direct": false,
"weight_type": "",
"vae_format": "",
"rng_type": "cuda",
"sampler_rng_type": "",
"prediction": "",
"lora_apply_mode": "auto",
"tae_preview_only": false,
"eager_load": false,
"rpc_servers": "",
"backend": "",
"params_backend": "",
"model_args": "",
"stream_layers": false,
"max_vram": 0,
"tensor_type_rules": ""
},
"memory": {
"system": {
"total_bytes": 34359738368,
"used_bytes": 17179869184,
"free_bytes": 17179869184,
"total_mb": 32768,
"used_mb": 16384,
"free_mb": 16384,
"usage_percent": 50.0
},
"process": {
"rss_bytes": 1073741824,
"virtual_bytes": 2147483648,
"rss_mb": 1024,
"virtual_mb": 2048
},
"gpu": {
"available": true,
"name": "NVIDIA GeForce RTX 4090",
"total_bytes": 25769803776,
"used_bytes": 8589934592,
"free_bytes": 17179869184,
"total_mb": 24576,
"used_mb": 8192,
"free_mb": 16384,
"usage_percent": 33.3
}
},
"features": {
"experimental_offload": false
}
}Response during model loading:
{
"status": "ok",
"version": "1.2.3",
"git_commit": "abc1234",
"model_loaded": false,
"model_loading": true,
"loading_model_name": "flux1-dev-Q4_K_S.gguf",
"loading_step": 3,
"loading_total_steps": 10,
"last_error": null,
"model_name": null,
"model_type": null,
"model_architecture": null,
"loaded_components": {},
"upscaler_loaded": false,
"upscaler_name": null,
"ws_enabled": true,
"load_options": {},
"memory": { "..." : "..." },
"features": {
"experimental_offload": false
}
}| Field | Type | Description |
|---|---|---|
status |
string | Always "ok" if server is running |
version |
string | Server version string |
git_commit |
string | Git commit hash of the build |
model_loaded |
boolean | Whether a model is currently loaded |
model_loading |
boolean | Whether a model is currently being loaded |
loading_model_name |
string|null | Name of model being loaded, or null if not loading |
loading_step |
integer | Current loading step (0 if not loading) |
loading_total_steps |
integer | Total loading steps (0 if not loading) |
last_error |
string|null | Last model load error message, or null if no error |
model_name |
string|null | Name of loaded model, or null if none |
model_type |
string|null | Type of loaded model (checkpoint or diffusion), or null |
model_architecture |
string|null | Auto-detected architecture (e.g., Z-Image, Flux, SDXL), or null |
loaded_components |
object | Component models loaded with the main model |
loaded_components.vae |
string|null | Loaded VAE model name |
loaded_components.clip_l |
string|null | Loaded CLIP-L model name |
loaded_components.clip_g |
string|null | Loaded CLIP-G model name |
loaded_components.t5xxl |
string|null | Loaded T5-XXL model name |
loaded_components.controlnet |
string|null | Loaded ControlNet model name |
loaded_components.llm |
string|null | Loaded LLM model name |
loaded_components.llm_vision |
string|null | Loaded LLM vision model name |
upscaler_loaded |
boolean | Whether an upscaler is currently loaded |
upscaler_name |
string|null | Name of loaded upscaler model, or null |
ws_enabled |
boolean | Whether the WebSocket endpoint is compiled in and listening (clients connect to ws://<host>:<port>/ws on the same port as the REST API) |
load_options |
object | The full set of load options the currently-loaded model was loaded with - the same field set as LoadOptions in /openapi.json (e.g. n_threads, flash_attn, diffusion_flash_attn, enable_mmap, vae_conv_direct, diffusion_conv_direct, weight_type, vae_format, rng_type, sampler_rng_type, prediction, lora_apply_mode, tae_preview_only, eager_load, rpc_servers, backend, params_backend, model_args, stream_layers, max_vram, tensor_type_rules). Empty object {} when no model is loaded. Useful for the WebUI to restore "Edit" form state from the actual server side. |
memory |
object | System, process, and GPU memory information (see Memory) |
features |
object | Feature flags |
features.experimental_offload |
boolean | Whether experimental VRAM offloading is compiled in |
Get detailed system, process, and GPU memory information.
Response:
{
"system": {
"total_bytes": 34359738368,
"used_bytes": 17179869184,
"free_bytes": 17179869184,
"total_mb": 32768,
"used_mb": 16384,
"free_mb": 16384,
"usage_percent": 50.0
},
"process": {
"rss_bytes": 1073741824,
"virtual_bytes": 2147483648,
"rss_mb": 1024,
"virtual_mb": 2048
},
"gpu": {
"available": true,
"name": "NVIDIA GeForce RTX 4090",
"total_bytes": 25769803776,
"used_bytes": 8589934592,
"free_bytes": 17179869184,
"total_mb": 24576,
"used_mb": 8192,
"free_mb": 16384,
"usage_percent": 33.3
}
}| Field | Type | Description |
|---|---|---|
system.total_bytes |
integer | Total system RAM in bytes |
system.used_bytes |
integer | Used system RAM in bytes |
system.free_bytes |
integer | Free system RAM in bytes |
system.total_mb |
integer | Total system RAM in MB |
system.used_mb |
integer | Used system RAM in MB |
system.free_mb |
integer | Free system RAM in MB |
system.usage_percent |
float | System RAM usage percentage (0-100) |
process.rss_bytes |
integer | Process Resident Set Size in bytes |
process.virtual_bytes |
integer | Process virtual memory in bytes |
process.rss_mb |
integer | Process RSS in MB |
process.virtual_mb |
integer | Process virtual memory in MB |
gpu.available |
boolean | Whether GPU info is available |
gpu.name |
string | GPU device name |
gpu.total_bytes |
integer | Total VRAM in bytes |
gpu.used_bytes |
integer | Used VRAM in bytes |
gpu.free_bytes |
integer | Free VRAM in bytes |
gpu.total_mb |
integer | Total VRAM in MB |
gpu.used_mb |
integer | Used VRAM in MB |
gpu.free_mb |
integer | Free VRAM in MB |
gpu.usage_percent |
float | VRAM usage percentage (0-100) |
List all available models organized by type. Supports optional filtering via query parameters.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
type |
string | Filter by model type: checkpoint, diffusion, vae, lora, clip, t5, embedding, controlnet, llm, esrgan |
extension |
string | Filter by file extension (e.g., safetensors, gguf, ckpt). Leading dot is optional. |
search |
string | Search in model name (case-insensitive substring match) |
name |
string | Alias for search parameter |
Example Requests:
GET /models # All models
GET /models?type=checkpoint # Only checkpoints
GET /models?extension=gguf # Only GGUF files
GET /models?search=flux # Models containing "flux" in name
GET /models?type=diffusion&extension=gguf # Diffusion models in GGUF format
GET /models?type=lora&search=detail # LoRAs with "detail" in name
Response (without filters):
{
"checkpoints": [
{
"name": "SD1x/realisticStockPhoto_v30SD15.safetensors",
"type": "checkpoint",
"file_extension": ".safetensors",
"size_bytes": 2132658480,
"hash": null,
"is_loaded": false
}
],
"diffusion_models": [ ... ],
"vae": [ ... ],
"loras": [ ... ],
"clip": [ ... ],
"t5": [ ... ],
"controlnets": [ ... ],
"llm": [ ... ],
"esrgan": [ ... ],
"taesd": [ ... ],
"embeddings": [],
"loaded_model": null,
"loaded_model_type": null
}Response (with filters applied):
When filters are applied, the response includes an applied_filters object:
{
"checkpoints": [],
"diffusion_models": [ ... ],
"...": "...",
"loaded_model": null,
"loaded_model_type": null,
"applied_filters": {
"type": "diffusion",
"search": "flux"
}
}Model Object:
| Field | Type | Description |
|---|---|---|
name |
string | Model name (relative path from model directory) |
type |
string | Model type: checkpoint, diffusion, vae, lora, clip, t5, embedding, controlnet, llm, esrgan, taesd |
file_extension |
string | File extension with dot (e.g., .safetensors, .gguf) |
size_bytes |
integer | File size in bytes |
hash |
string|null | SHA256 hash (null until computed) |
is_loaded |
boolean | Whether this model is currently loaded (only for checkpoint/diffusion) |
Response Fields:
| Field | Type | Description |
|---|---|---|
checkpoints |
array | Checkpoint models |
diffusion_models |
array | Diffusion models |
vae |
array | VAE models |
loras |
array | LoRA models |
clip |
array | CLIP models |
t5 |
array | T5 models |
controlnets |
array | ControlNet models |
llm |
array | LLM models for multimodal |
esrgan |
array | ESRGAN upscaler models |
taesd |
array | TAESD preview models |
embeddings |
array | Textual inversion embeddings |
loaded_model |
string|null | Currently loaded model name |
loaded_model_type |
string|null | Type of loaded model |
applied_filters |
object | (Optional) Shows which filters were applied |
Force a rescan of model directories.
Request Body: None required
Response (200):
{
"success": true,
"message": "Model list refreshed",
"models": [ ... ]
}Upload a model file from the client to the server's model directory. The file is written to the directory matching the chosen model_type (checkpoints/, diffusion_models/, vae/, lora/, etc.) under whatever the server config has set for that path.
Content-Type: multipart/form-data
Form fields:
| Field | Type | Required | Description |
|---|---|---|---|
file |
binary | Yes | The model file (.safetensors, .gguf, .ckpt, .pth, .bin, .pt accepted depending on type) |
model_type |
string | Yes | One of: checkpoint, diffusion, vae, lora, clip, t5, embedding, controlnet, llm, esrgan, taesd |
subfolder |
string | No | Optional subfolder under the type's root directory (e.g. SDXL, anime). Created if it doesn't exist. |
filename |
string | No | Override the destination filename. Defaults to the uploaded file's name. |
Success Response (200):
{
"success": true,
"model_name": "SDXL/my_model.safetensors",
"size_bytes": 6938041632
}Error Responses:
400— invalidmodel_type, missingfile, or filename collision (server refuses to overwrite an existing model — pre-rename or delete the existing one first).413— file exceeds the server's max upload size limit (configurable in the build).
After a successful upload the server triggers an internal model rescan, so the new file is immediately visible to subsequent GET /models calls.
Load a model into memory. Only one model can be loaded at a time.
Async vs synchronous behavior:
By default the call returns immediately with 202 Accepted and the actual sd.cpp load runs in a detached background thread (model loads can take minutes for large GGUFs). Completion is signalled via WebSocket events model_loading_progress, model_loaded, model_load_failed, or by polling GET /health for model_loading: false && model_loaded: true.
Pass ?wait=true to block the response until the load completes (or fails, or the timeout elapses). Useful for shell scripts that want a single blocking call.
Query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
wait |
bool | false |
If true, hold the HTTP response until the load finishes. Tolerates 1, true, yes, on. |
timeout |
int | 600 |
Seconds to wait before giving up (only honored when wait=true). Capped at 3600 (1 hour). On timeout the response is 504 and the load continues in the background. |
Concurrency: only one load can be in flight at a time. A second POST /models/load (with or without wait) returns 409 Conflict until the first finishes.
A model is already loaded: POST /models/load returns 409 Conflict if any model is currently loaded. To load a different model — or to reload the same one with different options — call POST /models/unload first, then POST /models/load. This guard prevents automation from accidentally re-loading on top of an in-use model. Example response:
{
"error": "A model is already loaded. Call POST /models/unload first, then POST /models/load.",
"loaded_model": "SD1x/realisticStockPhoto_v30SD15.safetensors"
}POST /models/unload is synchronous — it returns only after the model has been freed — so a script can safely chain it: unload && load. The WebUI's Load Model / Switch Model / Apply Changes buttons do exactly this internally, calling /models/unload and then /models/load in sequence.
Request Body:
{
"model_name": "SD1x/realisticStockPhoto_v30SD15.safetensors",
"model_type": "checkpoint",
"vae": "vae-ft-mse-840000-ema-pruned.safetensors",
"clip_l": "clip_l.safetensors",
"clip_g": "clip_g.safetensors",
"clip_vision": null,
"t5xxl": "t5xxl_fp16.safetensors",
"controlnet": "control_v11p_sd15_canny.safetensors",
"llm": "qwen_3_4b.Q8_0.gguf",
"llm_vision": null,
"taesd": null,
"high_noise_diffusion_model": null,
"photo_maker": null,
"options": {
"n_threads": 8,
"flash_attn": true,
"diffusion_flash_attn": true,
"backend": "te=cpu",
"params_backend": "",
"model_args": "",
"rng_type": "cuda",
"prediction": "",
"lora_apply_mode": "auto"
}
}Component Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model_name |
string | Yes | - | Model name from /models endpoint |
model_type |
string | No | "checkpoint" |
"checkpoint" or "diffusion" |
vae |
string | No | null | VAE model name to use |
clip_l |
string | No | null | CLIP-L model name (for Flux/SD3) |
clip_g |
string | No | null | CLIP-G model name (for SD3) |
clip_vision |
string | No | null | CLIP vision encoder for IP-Adapter |
t5xxl |
string | No | null | T5-XXL model name (for Flux/SD3) |
controlnet |
string | No | null | ControlNet model name |
llm |
string | No | null | LLM model name for multimodal (e.g., Qwen) |
llm_vision |
string | No | null | LLM vision model name (optional) |
taesd |
string | No | null | TAESD model name for preview |
high_noise_diffusion_model |
string | No | null | High-noise diffusion model for MoE |
photo_maker |
string | No | null | PhotoMaker model path |
Load Options (options object):
Per-field rationale + recommendations: the table below is a one-line summary. The full per-field documentation — what each option actually does inside sd.cpp, when to enable it, default rationale — lives in machine-readable form at
GET /options/descriptions(sourced fromdata/load_options.json). The WebUI's ModelLoad form fetches this on mount and surfaces every option's description as a hover tooltip, and an LLM agent can fetch it directly to reason about which option to set.
| Field | Type | Default | Description |
|---|---|---|---|
n_threads |
integer | -1 (auto) | Number of CPU threads |
vae_conv_direct |
boolean | false | Use ggml_conv2d_direct in VAE |
diffusion_conv_direct |
boolean | false | Use ggml_conv2d_direct in diffusion model |
flash_attn |
boolean | true | Enable Flash Attention for CLIP / T5 / conditioner |
diffusion_flash_attn |
boolean | false | Enable Flash Attention specifically for the diffusion model (UNet/DiT/Flux). sd.cpp keeps this separate from flash_attn because CUDA support and numerical stability used to differ between the two. |
enable_mmap |
boolean | true | Use memory-mapped file loading |
tae_preview_only |
boolean | false | Only use TAESD for preview, not final |
eager_load |
boolean | false | Eagerly move weights to the compute backend at load time |
weight_type |
string | "" | Weight type (f32, f16, q8_0, q5_0, q4_0) |
vae_format |
string | "" | Override VAE format detection |
tensor_type_rules |
string | "" | Per-tensor weight rules (e.g., "^vae\.=f16") |
rng_type |
string | "cuda" | RNG type: std_default, cuda, cpu |
sampler_rng_type |
string | "" | Sampler RNG (empty = use rng_type) |
prediction |
string | "" | Prediction type override (eps, v, edm_v, sd3_flow, flux_flow, flux2_flow, or empty for auto) |
lora_apply_mode |
string | "auto" | LoRA apply mode: auto, immediately, at_runtime |
rpc_servers |
string | "" | Comma-separated RPC backend endpoints |
backend |
string | "" | Per-component placement, e.g. "te=cpu", "te=cpu,vae=cpu,controlnet=cpu" to hold specific components on CPU RAM |
params_backend |
string | "" | Global params placement, e.g. "*=cpu" to hold model weights on CPU RAM |
model_args |
string | "" | Comma-separated architecture-specific key=value knobs (Chroma DiT/T5 masking, Qwen-Image conditioning, etc.). See /openapi.json for the current set. |
Layer streaming (requires -DSD_EXPERIMENTAL_OFFLOAD=ON build; check /health features.experimental_offload). Use these to run models that don't fit in VRAM by streaming diffusion layers one at a time:
| Field | Type | Default | Description |
|---|---|---|---|
stream_layers |
boolean | false | Enable per-layer streaming of the diffusion model |
max_vram |
number | 0 | Streaming VRAM budget in GiB. Pair with stream_layers: true; the streaming planner handles prefetch and eviction internally. |
Per-generation VAE tiling (vae_tiling, vae_tile_size_x/y, vae_tile_overlap) and flow_shift, circular_x, circular_y now live on the generation request, not on load. See the txt2img / img2img / txt2vid schemas in /openapi.json.
Success Response (202 Accepted) — async (default, no ?wait):
The load was accepted and dispatched to a background thread. Watch /health or the WebSocket events for completion.
{
"success": true,
"status": "loading",
"message": "Model load started. Watch /health.model_loading or the model_loading_progress / model_loaded WS events for completion. Pass ?wait=true to block until the load finishes.",
"model_name": "SD1x/realisticStockPhoto_v30SD15.safetensors",
"model_type": "checkpoint"
}Success Response (200) — synchronous (?wait=true, load completed before timeout):
{
"success": true,
"status": "loaded",
"model_name": "SD1x/realisticStockPhoto_v30SD15.safetensors",
"model_type": "checkpoint",
"architecture": "SD1"
}Failure Response (500) — synchronous (?wait=true, load failed within timeout):
The captured error from sd.cpp (CUDA OOM, weights mismatch, file not found, …) is echoed in error.
{
"success": false,
"status": "failed",
"error": "CUDA out of memory: tried to allocate 6.0 GiB on device 0",
"model_name": "..."
}Timeout Response (504) — synchronous (?wait=true, load still in progress):
The load continues in the background — the timeout only abandons the wait. Subsequent /health polls or WS events will report final state.
{
"success": false,
"status": "loading",
"error": "Model load did not finish within 600 seconds. Loading continues in the background — poll /health or watch the model_loaded WebSocket event.",
"model_name": "...",
"timeout_sec": 600
}Error Response (400):
{
"error": "model_name is required"
}Conflict Response (409) — another load is already running:
{
"error": "Another model is already loading. Wait for it to finish or call /models/unload first."
}Example (blocking shell script using ?wait=true):
curl -s -X POST 'http://localhost:8080/models/load?wait=true&timeout=900' \
-H 'Content-Type: application/json' \
-d '{"model_name":"SD1x/example.safetensors","model_type":"checkpoint"}' \
| jq -e '.success' >/dev/null && echo "loaded" || echo "load failed"Unload the currently loaded model from memory.
Request Body: None required
Success Response (200):
{
"success": true,
"message": "Model unloaded"
}Compute SHA256 hash of a model file. The hash is cached after first computation.
URL Parameters:
| Parameter | Description |
|---|---|
model_type |
One of: checkpoint, diffusion, vae, lora, clip, t5, embedding, controlnet, llm, esrgan |
model_name |
Model name (URL-encoded if contains /) |
Example:
GET /models/hash/checkpoint/SD1x%2FrealisticStockPhoto_v30SD15.safetensors
Success Response (200):
{
"model_name": "SD1x/realisticStockPhoto_v30SD15.safetensors",
"model_type": "checkpoint",
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
}Error Response (404):
{
"error": "Model not found: invalid_model.safetensors"
}All generation endpoints add jobs to a FIFO queue. Jobs are processed sequentially by a background worker.
ControlNet allows you to guide image generation with control images (edge maps, depth maps, poses, etc.).
Setup:
- Load a ControlNet model with your main model using the
/models/loadendpoint:
{
"model_name": "v1-5-pruned.safetensors",
"controlnet": "control_v11p_sd15_canny.safetensors"
}- Provide a pre-processed control image with your generation request:
{
"prompt": "a beautiful house",
"control_image_base64": "<base64-encoded-canny-edge-image>",
"control_strength": 0.9
}Important Notes:
- You must pre-process your control images before sending them (e.g., apply Canny edge detection, extract depth maps, etc.)
- The control image will be automatically resized to match the output dimensions
control_strengthcontrols how much the ControlNet influences the output (0.0 = no influence, 1.0 = full influence)- ControlNet can be attached, swapped, or detached at runtime without reloading the base model - see "ControlNet hot-swap" below.
Since the upstream sd_ctx_load_control_net API landed, the base model no longer has to be reloaded to change ControlNet. Do not call these while a generation is in flight.
POST /controlnet/loadbody{"controlnet": "<name>"}- attach or swap. Name comes from/models?type=controlnet.POST /controlnet/unload- detach.GET /controlnet/status-{"loaded": bool, "name": string | null}.
The features.controlnet_hotswap flag in /health advertises support.
SD 1.5 backbones can be augmented with a motion module (AnimateDiff v2/v3, PiD 1.5) at load time. Pass motion_module alongside the base model in /models/load:
{
"model_name": "v1-5-pruned.safetensors",
"motion_module": "mm_sd15_v3.safetensors"
}When a motion module is present, POST /txt2vid and POST /img2img (init_image + video_frames > 1) become available for that context.
POST /adetailer runs a YOLOv8 detector + inpainting pass over an input image using the currently loaded model. Detector files live under paths.adetailer and are listed via /models?type=adetailer.
LoRAs are specified directly in the prompt using the sd.cpp syntax:
<lora:filename:weight>
Examples:
<lora:add_detail:0.8>- Apply add_detail.safetensors with weight 0.8<lora:style_anime:1.0>- Apply style_anime.safetensors with weight 1.0
You can use multiple LoRAs in a single prompt:
a beautiful landscape <lora:add_detail:0.8> <lora:enhance_colors:0.5>
Generate images from text prompt.
Prerequisites: A model must be loaded.
Request Body:
{
"prompt": "a lovely cat holding a sign that says hello world <lora:add_detail:0.8>",
"negative_prompt": "blurry, low quality, bad anatomy",
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0,
"seed": -1,
"sampler": "euler_a",
"scheduler": "discrete",
"batch_count": 1,
"clip_skip": -1,
"control_image_base64": "/9j/4AAQSkZJRg...",
"control_strength": 0.9
}Parameters:
Per-field rationale + recommendations: the table below is a one-line summary. Full per-field documentation for every generation parameter (across
/txt2img,/img2img,/txt2vid,/upscale) is served atGET /options/generation(sourced fromdata/generation_options.json). The WebUI fetches it on mount and surfaces each field's description as a hover tooltip; an LLM agent can fetch it directly to pick the right values for a given architecture.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prompt |
string | Yes | - | Text prompt for generation (supports <lora:name:weight> syntax) |
title |
string | No | "" |
Optional display title attached to the queue job. Shown next to the type label in the WebUI Queue card and returned on /queue items. Stripped from params before strict validation runs, so it does not pollute the generation parameters. |
negative_prompt |
string | No | "" |
Negative prompt |
width |
integer | No | 512 | Image width in pixels |
height |
integer | No | 512 | Image height in pixels |
steps |
integer | No | 20 | Number of sampling steps |
cfg_scale |
float | No | 7.0 | Classifier-free guidance scale |
distilled_guidance |
float | No | 3.5 | Distilled guidance for Flux/distilled models |
eta |
float | No | 0.0 | Eta for DDIM/TCD samplers |
shifted_timestep |
integer | No | 0 | Shifted timestep for NitroFusion (250-500) |
seed |
integer | No | -1 | Random seed (-1 for random) |
sampler |
string | No | "euler_a" |
Sampling method (see Samplers) |
scheduler |
string | No | "discrete" |
Scheduler type (see Schedulers) |
batch_count |
integer | No | 1 | Number of images to generate |
clip_skip |
integer | No | -1 | CLIP skip layers (-1 for default) |
slg_scale |
float | No | 0.0 | Skip Layer Guidance scale (0 = disabled, 2.5 for SD3.5 medium) |
skip_layers |
array | No | [7,8,9] | Layers to skip for SLG |
slg_start |
float | No | 0.01 | SLG start percent |
slg_end |
float | No | 0.2 | SLG end percent |
custom_sigmas |
array | No | [] | Custom sigma schedule (overrides scheduler) |
ref_images |
array | No | [] | Array of base64-encoded reference images (Flux Kontext) |
ref_image_args |
string | No | "" | Comma-separated k=v flags for reference-image processing (e.g. resize_before_vae=0,ref_index_mode=increase) - see upstream sd.cpp docs |
control_image_base64 |
string | No | - | Base64-encoded pre-processed control image (requires ControlNet) |
control_strength |
float | No | 0.9 | ControlNet influence strength (0.0 - 1.0) |
vae_tiling |
boolean | No | false | Enable VAE tiling for large images |
vae_tile_size_x |
integer | No | 0 | VAE tile X size (0 = auto) |
vae_tile_size_y |
integer | No | 0 | VAE tile Y size (0 = auto) |
vae_tile_overlap |
float | No | 0.5 | VAE tile overlap |
easycache |
boolean | No | false | Enable caching for DiT models (speeds up generation) |
easycache_threshold |
float | No | 0.2 | Cache reuse threshold (higher = more reuse, lower quality) |
easycache_start |
float | No | 0.15 | Cache start percent |
easycache_end |
float | No | 0.95 | Cache end percent |
pm_id_images |
array | No | [] | Array of base64-encoded PhotoMaker ID images |
pm_id_embed_path |
string | No | "" | Path to PhotoMaker ID embedding file |
pm_style_strength |
float | No | 20.0 | PhotoMaker style strength |
upscale |
boolean | No | false | Enable upscaling after generation (requires upscaler loaded) |
upscale_repeats |
integer | No | 1 | Number of times to run upscaler |
upscale_auto_unload |
boolean | No | true | Unload upscaler after use |
expand_prompt |
boolean | No | false | If true, parse prompt for dynamic-prompts syntax ({a|b|c}, {N$$a|b|c}) and create one queue item per variation. See Prompt Expansion below. |
When expand_prompt: true is set, the server parses the prompt field for inline dynamic-prompts syntax (a1111-DynamicPrompts compatible) and expands it combinatorially into multiple queue items, all sharing a single variation_group_id.
Supported syntax:
| Form | Meaning | Example | Expands to |
|---|---|---|---|
{a|b|c} |
Alternation — one item per choice | {red|blue} apple |
2 prompts |
{N$$a|b|c|d} |
Pick-N — one item per N-element subset, joined by , |
{2$$a1|a2|a3} |
3 prompts (a1, a2, a1, a3, a2, a3) |
| Nested | Any {…} may contain further {…} |
{cat|{red|blue} dog} |
3 prompts |
| Escapes | \{, \}, |, \\ |
a \{literal\} brace |
1 prompt with literal braces |
Hard limit: a single request may not produce more than 200 variations — the server returns 400 with a clear error otherwise.
The expansion submission response is shaped differently from a regular submission. It carries group_id, variation_count, and job_ids[] instead of a single job_id:
{
"group_id": "8a2f3d61-1c4e-4b87-9d2a-3f7e0c5d4e92",
"variation_count": 12,
"job_ids": [
"9b7c1d3e-...", "...", ...
],
"status": "pending",
"position": 12
}Each created job has these extra fields under params:
variation_group_id(string) — the same UUID asgroup_id.variation_index(integer) — 0-based position of this variation in the expansion.variation_total(integer) — count of variations in the group.variation_template(string) — the original templated prompt (for UI display).
When the server-side toggle output_group_folders is on (default; see Output Settings), each grouped job's outputs land in <output>/<group_id>/<job_id>/ instead of the flat <output>/<job_id>/. URLs returned in the job's outputs array reflect that path.
Success Response (202 Accepted) — single-prompt submission (no expand_prompt, or prompt has no template syntax):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"position": 1
}Success Response (202 Accepted) — expand_prompt: true:
{
"group_id": "8a2f3d61-1c4e-4b87-9d2a-3f7e0c5d4e92",
"variation_count": 4,
"job_ids": [
"9b7c1d3e-aaaa-...",
"9b7c1d3e-bbbb-...",
"9b7c1d3e-cccc-...",
"9b7c1d3e-dddd-..."
],
"status": "pending",
"position": 4
}Error Response (400):
{
"error": "No model loaded"
}Other 400 errors specific to expansion:
"Prompt template error: <detail>"— malformed syntax (mismatched braces, pick-N count > options)."Prompt template would create N jobs, exceeds limit of 200..."— explosion guard tripped."Prompt template expanded to 0 variations"— degenerate template.
Generate images from an existing image and text prompt.
Prerequisites: A model must be loaded.
Request Body:
{
"prompt": "watercolor painting style <lora:watercolor:0.9>",
"negative_prompt": "photo, realistic",
"init_image_base64": "/9j/4AAQSkZJRg...",
"strength": 0.75,
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0,
"seed": -1,
"sampler": "euler_a",
"scheduler": "discrete",
"batch_count": 1,
"clip_skip": -1
}Additional Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
init_image_base64 |
string | Yes | - | Base64-encoded input image (JPEG/PNG) |
strength |
float | No | 0.75 | Denoising strength (0.0 - 1.0) |
img_cfg_scale |
float | No | -1.0 | Image CFG scale for instruct-pix2pix (-1 = same as cfg_scale) |
mask_image_base64 |
string | No | - | Base64-encoded mask image for inpainting (white = repaint, black = keep) |
control_image_base64 |
string | No | - | Base64-encoded pre-processed control image (requires ControlNet) |
control_strength |
float | No | 0.9 | ControlNet influence strength (0.0 - 1.0) |
expand_prompt |
boolean | No | false | If true, parse prompt for {a|b|c} / {N$$a|b|c} syntax and create one queue item per variation. See Prompt Expansion under /txt2img. |
title |
string | No | "" |
Optional display title attached to the queue job (same semantics as /txt2img). |
All other parameters are the same as Text to Image including advanced guidance (SLG, distilled_guidance), reference images, VAE tiling, EasyCache, PhotoMaker, upscaling, and the expand_prompt flag.
Success Response (202 Accepted) — single-prompt submission:
{
"job_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "pending",
"position": 1
}When expand_prompt: true, the response contains group_id, variation_count, and job_ids[] instead of job_id — see Prompt Expansion under /txt2img for details.
Generate video frames from text prompt (requires video-capable model like Wan).
Prerequisites: A video-capable model must be loaded.
Request Body:
{
"prompt": "a cat walking through a garden, cinematic",
"negative_prompt": "static, blurry, low quality",
"width": 832,
"height": 480,
"video_frames": 33,
"steps": 30,
"cfg_scale": 6.0,
"seed": -1,
"sampler": "euler",
"scheduler": "discrete",
"flow_shift": 3.0,
"clip_skip": -1
}Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prompt |
string | Yes | - | Text prompt for generation (supports <lora:name:weight> syntax) |
title |
string | No | "" |
Optional display title attached to the queue job (same semantics as /txt2img). |
negative_prompt |
string | No | "" |
Negative prompt |
width |
integer | No | 832 | Video width in pixels |
height |
integer | No | 480 | Video height in pixels |
video_frames |
integer | No | 33 | Number of frames to generate |
fps |
integer | No | 16 | Video frames per second (metadata) |
steps |
integer | No | 30 | Number of sampling steps |
cfg_scale |
float | No | 6.0 | Classifier-free guidance scale |
distilled_guidance |
float | No | 3.5 | Distilled guidance for Flux/distilled models |
eta |
float | No | 0.0 | Eta for DDIM/TCD samplers |
seed |
integer | No | -1 | Random seed (-1 for random) |
sampler |
string | No | "euler" |
Sampling method |
scheduler |
string | No | "discrete" |
Scheduler type |
flow_shift |
float | No | 3.0 | Flow shift for video generation |
clip_skip |
integer | No | -1 | CLIP skip layers |
slg_scale |
float | No | 0.0 | Skip Layer Guidance scale |
skip_layers |
array | No | [7,8,9] | Layers to skip for SLG |
slg_start |
float | No | 0.01 | SLG start percent |
slg_end |
float | No | 0.2 | SLG end percent |
init_image_base64 |
string | No | - | Base64-encoded init image (first frame for vid2vid) |
end_image_base64 |
string | No | - | Base64-encoded end image (for FLF2V) |
strength |
float | No | 0.75 | Denoising strength for vid2vid |
control_image_base64 |
string | No | - | Base64-encoded control image (applies to all frames) |
control_frames |
array | No | [] | Array of base64-encoded control frames |
high_noise_steps |
integer | No | -1 | High-noise phase steps (-1 = auto, for MoE models) |
high_noise_cfg_scale |
float | No | 7.0 | CFG scale for high-noise phase |
high_noise_sampler |
string | No | "" | Sampler for high-noise phase (empty = use main) |
high_noise_distilled_guidance |
float | No | 3.5 | Distilled guidance for high-noise phase |
high_noise_slg_scale |
float | No | 0.0 | SLG scale for high-noise phase |
high_noise_skip_layers |
array | No | [7,8,9] | Skip layers for high-noise SLG |
high_noise_slg_start |
float | No | 0.01 | High-noise SLG start percent |
high_noise_slg_end |
float | No | 0.2 | High-noise SLG end percent |
moe_boundary |
float | No | 0.875 | Timestep boundary for MoE models |
vace_strength |
float | No | 1.0 | WAN VACE strength |
easycache |
boolean | No | false | Enable caching for DiT models |
easycache_threshold |
float | No | 0.2 | Cache reuse threshold |
easycache_start |
float | No | 0.15 | Cache start percent |
easycache_end |
float | No | 0.95 | Cache end percent |
expand_prompt |
boolean | No | false | If true, parse prompt for {a|b|c} / {N$$a|b|c} syntax and create one queue item per variation. See Prompt Expansion under /txt2img. |
Success Response (202 Accepted) — single-prompt submission:
{
"job_id": "550e8400-e29b-41d4-a716-446655440002",
"status": "pending",
"position": 1
}When expand_prompt: true, the response contains group_id, variation_count, and job_ids[] instead of job_id — see Prompt Expansion under /txt2img.
Upscale an image using ESRGAN. Requires an upscaler model to be loaded.
Request Body:
{
"image_base64": "/9j/4AAQSkZJRg...",
"upscale_factor": 4,
"tile_size": 128,
"repeats": 1
}Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
image_base64 |
string | Yes | - | Base64-encoded input image |
title |
string | No | "" |
Optional display title attached to the queue job (same semantics as /txt2img). |
upscale_factor |
integer | No | 4 | Target upscale factor |
tile_size |
integer | No | 128 | Tile size for processing (VRAM optimization) |
repeats |
integer | No | 1 | Run upscaler multiple times |
Success Response (202 Accepted):
{
"job_id": "550e8400-e29b-41d4-a716-446655440003",
"status": "pending",
"position": 1
}Load an ESRGAN upscaler model.
Request Body:
{
"model_name": "RealESRGAN_x4plus.pth",
"n_threads": -1,
"tile_size": 128
}Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model_name |
string | Yes | - | ESRGAN model name from /models |
n_threads |
integer | No | -1 (auto) | Number of CPU threads |
tile_size |
integer | No | 128 | Tile size for processing |
Success Response (200):
{
"success": true,
"message": "Upscaler loaded successfully",
"model_name": "RealESRGAN_x4plus.pth",
"upscale_factor": 4
}Unload the currently loaded upscaler model.
Request Body: None required
Success Response (200):
{
"success": true,
"message": "Upscaler unloaded"
}Get jobs with filtering, pagination, and optional date grouping.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
status |
string | "all" |
Filter by status: pending, processing, completed, failed, cancelled, all |
type |
string | "all" |
Filter by type: txt2img, img2img, txt2vid, upscale, convert, model_download, model_hash, all |
search |
string | - | Search in prompt/negative_prompt (case-insensitive) |
architecture |
string | - | Filter by model architecture (case-insensitive partial match) |
limit |
integer | 20 | Maximum items per page |
page |
integer | 1 | Page number (1-based). Mutually exclusive with offset. |
offset |
integer | 0 | Items to skip (0-based item index). Mutually exclusive with page. |
before |
integer | - | Items created before this Unix timestamp |
after |
integer | - | Items created after this Unix timestamp |
group_by |
string | - | Group response by date |
Pagination — pick one cursor style, not both:
Page-based (most callers):
GET /queue?limit=20&page=1
GET /queue?limit=20&page=2
GET /queue?limit=20&page=5
Offset-based (cursor-style scrolling):
GET /queue?limit=20&offset=0
GET /queue?limit=20&offset=20
GET /queue?limit=20&offset=40
Sending both page and offset in the same request returns HTTP 400 — they're equivalent expressions of the same cursor, and accepting both would silently let one override the other. Use page or offset, not both. When neither is supplied, the server returns page 1 (offset=0).
The response payload echoes back the resolved page, offset, limit, total_pages, has_more, and has_prev — drive the next request off those rather than assuming.
pagein the response is approximate when you paginated by rawoffset. It's computed as(offset / limit) + 1(integer division). If youroffsethappens to be a clean page boundary (a multiple oflimit),pageis exact. Otherwise it rounds down — e.g.?offset=5&limit=3returnspage: 2even though the window starts mid-page-2.offsetis the ground truth; treatpageas informational only when not using the page-based API.
Response (standard pagination):
{
"pending_count": 2,
"processing_count": 1,
"completed_count": 5,
"failed_count": 0,
"cancelled_count": 0,
"total_count": 8,
"filtered_count": 8,
"offset": 0,
"limit": 20,
"has_more": false,
"page": 1,
"total_pages": 1,
"has_prev": false,
"newest_timestamp": 1704067260,
"oldest_timestamp": 1704067200,
"applied_filters": {},
"items": [
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "txt2img",
"status": "completed",
"progress": {
"step": 20,
"total_steps": 20
},
"created_at": "2024-01-01T12:00:00Z",
"started_at": "2024-01-01T12:00:05Z",
"completed_at": "2024-01-01T12:01:00Z",
"outputs": [
"550e8400-e29b-41d4-a716-446655440000/image_0.png"
],
"params": {
"prompt": "a lovely cat",
"width": 512,
"height": 512
},
"model_settings": {
"model_name": "v1-5-pruned.safetensors",
"model_architecture": "SD1"
}
}
]
}Response (with group_by=date):
{
"groups": [
{
"date": "2024-01-01",
"label": "Today",
"timestamp": 1704067200,
"count": 3,
"items": [ ... ]
}
],
"total_count": 5,
"page": 1,
"total_pages": 2,
"limit": 20,
"has_more": true,
"has_prev": false,
"group_by": "date",
"applied_filters": {}
}Queue Status Fields:
| Field | Type | Description |
|---|---|---|
pending_count |
integer | Jobs waiting to be processed |
processing_count |
integer | Jobs currently being processed (0 or 1) |
completed_count |
integer | Successfully completed jobs |
failed_count |
integer | Failed jobs |
cancelled_count |
integer | Cancelled jobs |
total_count |
integer | Total jobs in history |
filtered_count |
integer | Total matching the current filter |
offset |
integer | Current pagination offset |
limit |
integer | Current page size limit |
has_more |
boolean | Whether more items exist after current page |
page |
integer | Current page number |
total_pages |
integer | Total number of pages |
has_prev |
boolean | Whether previous pages exist |
newest_timestamp |
integer | Unix timestamp of newest item |
oldest_timestamp |
integer | Unix timestamp of oldest item |
applied_filters |
object | Active filter values |
Get status of a specific job.
URL Parameters:
| Parameter | Description |
|---|---|
job_id |
Job UUID |
Success Response (200):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "txt2img",
"status": "completed",
"progress": {
"step": 20,
"total_steps": 20
},
"created_at": "2024-01-01T12:00:00Z",
"started_at": "2024-01-01T12:00:05Z",
"completed_at": "2024-01-01T12:01:00Z",
"outputs": [
"550e8400-e29b-41d4-a716-446655440000/image_0.png"
],
"params": {
"prompt": "a lovely cat",
"negative_prompt": "",
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0,
"seed": 12345,
"sampler": "euler_a",
"scheduler": "discrete",
"batch_count": 1,
"clip_skip": -1
},
"model_settings": {
"model_name": "v1-5-pruned.safetensors",
"model_architecture": "SD1",
"clip_l_model": null,
"clip_g_model": null,
"t5xxl_model": null,
"vae_model": null,
"controlnet_model": null,
"lora_model": null
}
}Job Object Fields:
| Field | Type | Description |
|---|---|---|
job_id |
string | Unique job identifier (UUID) |
type |
string | Job type: txt2img, img2img, txt2vid, upscale, convert, model_download, model_hash |
status |
string | Job status (see Job Status) |
progress |
object | Current progress information |
progress.step |
integer | Current step within current image |
progress.total_steps |
integer | Total steps per image |
created_at |
string | ISO 8601 timestamp when job was created |
started_at |
string | ISO 8601 timestamp when processing started (if applicable) |
completed_at |
string | ISO 8601 timestamp when job completed (if applicable) |
outputs |
array | List of output file paths (relative to /output/) |
output_urls |
array | Absolute URLs to each output file. Built from the request's Host header by default; promoted to X-Forwarded-Host / X-Forwarded-Proto when the connecting peer is in server.trusted_proxies. Direct GETs to these URLs work without auth when auth.allow_public_outputs is true (default). |
params |
object | Original request parameters |
model_settings |
object | Model configuration at job creation time |
linked_job_id |
string | ID of linked job (e.g., hash job linked to download job) |
title |
string | User-supplied display title (only present when set at submission time). |
error |
string | Error message (only if status is failed) |
Error Response (404):
{
"error": "Job not found"
}Cancel a pending job. Only jobs with pending status can be cancelled.
URL Parameters:
| Parameter | Description |
|---|---|
job_id |
Job UUID |
Success Response (200):
{
"success": true,
"message": "Job cancelled"
}Error Response (400):
{
"error": "Cannot cancel job (not found or already processing)"
}Delete multiple jobs at once (soft-delete to recycle bin).
Request Body:
{
"job_ids": ["uuid1", "uuid2", "uuid3"]
}Success Response (200):
{
"success": true,
"deleted": 2,
"failed": 1,
"total": 3,
"failed_job_ids": ["uuid3"]
}Error Response (400):
{
"error": "job_ids array is required in request body"
}Get the current live preview image for a processing job. Returns a JPEG image, not JSON.
URL Parameters:
| Parameter | Description |
|---|---|
job_id |
Job UUID |
Response Headers:
| Header | Description |
|---|---|
Content-Type |
image/jpeg |
Cache-Control |
no-cache |
X-Preview-Width |
Preview image width |
X-Preview-Height |
Preview image height |
X-Preview-Step |
Current generation step |
Response Body: Binary JPEG image data
Error Response (404): No preview available
Jobs are soft-deleted to a recycle bin and auto-purged after a configurable retention period.
Get all soft-deleted jobs.
Response (200):
{
"success": true,
"enabled": true,
"retention_minutes": 1440,
"count": 2,
"items": [
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "txt2img",
"status": "deleted",
"progress": { "step": 20, "total_steps": 20 },
"created_at": "2024-01-01T08:00:00Z",
"started_at": "2024-01-01T08:01:00Z",
"completed_at": "2024-01-01T08:05:00Z",
"outputs": ["image_0.png"],
"params": { ... },
"model_settings": { ... },
"deleted_at": "2024-01-01T10:30:00Z",
"previous_status": "completed"
}
]
}| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether recycle bin is enabled |
retention_minutes |
integer | Auto-purge after this many minutes |
count |
integer | Number of items in recycle bin |
items[].deleted_at |
string | ISO 8601 timestamp when deleted |
items[].previous_status |
string | Status before deletion |
Restore a job from the recycle bin to its previous status.
Request Body: None required
Success Response (200):
{
"success": true,
"message": "Job restored from recycle bin"
}Error Response (400):
{
"error": "Cannot restore job (not found or not in recycle bin)"
}Permanently delete a job from the recycle bin.
Success Response (200):
{
"success": true,
"message": "Job permanently deleted"
}Error Response (400):
{
"error": "Cannot purge job (not found or still processing)"
}Permanently delete all jobs in the recycle bin.
Success Response (200):
{
"success": true,
"purged": 5,
"message": "Recycle bin cleared"
}Get recycle bin configuration.
Response (200):
{
"enabled": true,
"retention_minutes": 1440
}Control live preview generation during image/video generation.
Get current preview settings.
Response (200):
{
"enabled": true,
"mode": "tae",
"interval": 1,
"max_size": 256,
"quality": 75
}| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether preview generation is enabled |
mode |
string | Preview mode: none, proj, tae, vae |
interval |
integer | Generate preview every N steps |
max_size |
integer | Maximum preview dimension in pixels |
quality |
integer | JPEG quality (1-100) |
Preview Modes:
| Mode | Description |
|---|---|
none |
Previews disabled |
proj |
Fast projection-based preview (low quality) |
tae |
TAESD tiny autoencoder (balanced speed/quality) |
vae |
Full VAE preview (high quality, slower) |
Update preview settings.
Request Body:
{
"enabled": true,
"mode": "tae",
"interval": 1,
"max_size": 256,
"quality": 75
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean | No | true | Enable/disable previews |
mode |
string | No | "tae" | Preview mode |
interval |
integer | No | 1 | Steps between previews (1-100) |
max_size |
integer | No | 256 | Max preview size (64-1024) |
quality |
integer | No | 75 | JPEG quality (1-100) |
Success Response (200):
{
"success": true,
"settings": {
"enabled": true,
"mode": "tae",
"interval": 1,
"max_size": 256,
"quality": 75
}
}Server-side persistence for generation defaults and UI preferences.
Get generation defaults for all modes.
Response (200):
{
"txt2img": { ... },
"img2img": { ... },
"txt2vid": { ... }
}Update generation defaults for all modes.
Request Body:
{
"txt2img": { ... },
"img2img": { ... },
"txt2vid": { ... }
}Success Response (200):
{
"success": true,
"settings": {
"txt2img": { ... },
"img2img": { ... },
"txt2vid": { ... }
}
}Get generation defaults for a specific mode.
URL Parameters:
| Parameter | Description |
|---|---|
mode |
Generation mode: txt2img, img2img, txt2vid |
Response (200): Mode-specific generation defaults object.
Update generation defaults for a specific mode.
URL Parameters:
| Parameter | Description |
|---|---|
mode |
Generation mode: txt2img, img2img, txt2vid |
Request Body: Mode-specific generation defaults object.
Success Response (200):
{
"success": true,
"mode": "txt2img",
"preferences": { ... }
}Get UI preferences.
Response (200):
{
"desktop_notifications": true,
"theme": "dark",
"theme_custom": null
}Update UI preferences.
Request Body:
{
"desktop_notifications": true,
"theme": "dark",
"theme_custom": null
}Success Response (200):
{
"success": true,
"preferences": {
"desktop_notifications": true,
"theme": "dark",
"theme_custom": null
}
}Reset all settings to defaults.
Request Body: None required
Success Response (200):
{
"success": true,
"message": "Settings reset to defaults"
}Server-side toggle controlling how outputs of prompt-expansion jobs are organized on disk.
Success Response (200):
{
"output_group_folders": true
}| Field | Type | Description |
|---|---|---|
output_group_folders |
boolean | When true, jobs created via expand_prompt write outputs into <output_dir>/<group_id>/<job_id>/ instead of the flat <output_dir>/<job_id>/. Default true. |
Toggle the per-group output folder structure at runtime. The change applies to subsequently created jobs only — already-completed jobs keep their existing on-disk locations.
Request Body:
{
"output_group_folders": false
}Success Response (200):
{
"output_group_folders": false
}Browse output directory or serve generated files. Returns an interactive HTML file browser for directories, or the raw file content for files.
Path Parameters:
| Parameter | Description |
|---|---|
path |
(Optional) Relative path within output directory |
Query Parameters (for directories only):
| Parameter | Type | Default | Description |
|---|---|---|---|
sort |
string | name |
Sort column: name, size, date |
order |
string | asc |
Sort order: asc, desc |
Directory Response (200):
- Content-Type:
text/html - Returns interactive HTML file browser with thumbnails, sorting, and lightbox
File Response (200):
- Content-Type: Determined by file extension
- Returns raw file content
Error Responses:
- 400 Bad Request: Invalid path (path traversal attempt with
..) - 404 Not Found: Path does not exist
Get thumbnail for an image or video file.
Path Parameters:
| Parameter | Description |
|---|---|
path |
Relative path to image/video file within output directory |
Response (200):
- For images: 120x120 center-cropped JPEG thumbnail
- For videos: SVG play button placeholder
- Thumbnails are cached in
.thumbssubdirectories
Supported Formats:
- Images: .png, .jpg, .jpeg, .gif, .webp, .bmp
- Videos: .mp4, .webm, .avi, .mov, .mkv
Error Responses:
- 400 Bad Request: Invalid path, path traversal, or not a media file
- 404 Not Found: File does not exist
Output Directory Structure:
/output/
├── {job_id}/
│ ├── image_0.png # First generated image
│ ├── image_1.png # Second image (if batch_count > 1)
│ ├── config.json # Generation parameters
│ └── .thumbs/ # Cached thumbnails
│ └── image_0.jpg
├── {job_id}/ # Video job
│ ├── video.mp4 # Generated video
│ └── config.json
└── queue_state.json # Queue persistence file (internal)
Get available generation options including samplers, schedulers, and quantization types.
Response:
{
"samplers": [
"euler", "euler_a", "heun", "dpm2", "dpm++2s_a", "dpm++2m", "dpm++2mv2",
"ipndm", "ipndm_v", "lcm", "ddim_trailing", "tcd", "res_multistep", "res_2s",
"er_sde", "euler_cfg_pp", "euler_a_cfg_pp", "euler_ge"
],
"schedulers": [
"discrete", "karras", "exponential", "ays", "gits", "sgm_uniform",
"simple", "smoothstep", "kl_optimal", "lcm", "bong_tangent", "ltx2"
],
"quantization_types": [
{"id": "f32", "name": "F32 (32-bit float)", "bits": 32},
{"id": "f16", "name": "F16 (16-bit float)", "bits": 16},
{"id": "bf16", "name": "BF16 (Brain float 16)", "bits": 16},
{"id": "q8_0", "name": "Q8_0 (8-bit)", "bits": 8},
{"id": "q5_0", "name": "Q5_0 (5-bit)", "bits": 5},
{"id": "q5_1", "name": "Q5_1 (5-bit)", "bits": 5},
{"id": "q4_0", "name": "Q4_0 (4-bit)", "bits": 4},
{"id": "q4_1", "name": "Q4_1 (4-bit)", "bits": 4},
{"id": "q4_k", "name": "Q4_K (4-bit K-quant)", "bits": 4},
{"id": "q5_k", "name": "Q5_K (5-bit K-quant)", "bits": 5},
{"id": "q6_k", "name": "Q6_K (6-bit K-quant)", "bits": 6},
{"id": "q8_k", "name": "Q8_K (8-bit K-quant)", "bits": 8},
{"id": "q3_k", "name": "Q3_K (3-bit K-quant)", "bits": 3},
{"id": "q2_k", "name": "Q2_K (2-bit K-quant)", "bits": 2}
]
}Get detailed descriptions of all load options including labels, types, defaults, and recommended values. Returns the contents of the data/load_options.json configuration file.
Response (200):
{
"options": {
"flash_attn": {
"label": "Flash Attention",
"description": "Enable Flash Attention for faster inference",
"type": "boolean",
"default": true
}
},
"categories": { ... }
}Convert a model to GGUF format with specified quantization.
Request:
{
"input_path": "/path/to/model.safetensors",
"output_type": "q8_0",
"output_path": "/path/to/output.gguf",
"vae_path": "/path/to/vae.gguf",
"tensor_type_rules": "string"
}| Field | Type | Required | Description |
|---|---|---|---|
input_path |
string | Yes | Path to input model file |
output_type |
string | Yes | Quantization type (see /options for list) |
output_path |
string | No | Output path (auto-generated if not provided) |
vae_path |
string | No | VAE to bake into the model |
tensor_type_rules |
string | No | Custom tensor quantization rules |
title |
string | No | Optional display title attached to the queue job (same semantics as /txt2img). |
Response (202 Accepted):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"position": 1,
"output_path": "/path/to/output.q8_0.gguf"
}LLM-powered assistant for the Web UI with context awareness. Requires building with -DSDCPP_ASSISTANT_ENABLED=ON.
Send a message to the LLM assistant with application context.
Request:
{
"message": "Load the SDXL model",
"context": {
"current_view": "dashboard",
"settings": {},
"model_info": {},
"available_models": {}
}
}| Field | Type | Required | Description |
|---|---|---|---|
message |
string | Yes | User message |
context |
object | No | Application state context |
Response (200):
{
"success": true,
"message": "I'll load the SDXL model for you. Here's what I'm doing...",
"thinking": "The user wants to load an SDXL model...",
"tool_calls": [
{
"name": "load_model",
"parameters": { ... },
"result": "Model loaded successfully",
"executed_on_backend": true
}
],
"actions": [
{
"type": "load_model",
"parameters": {
"model_name": "sdxl-model.safetensors",
"model_type": "checkpoint"
}
}
]
}| Field | Type | Description |
|---|---|---|
success |
boolean | Whether the request succeeded |
message |
string | Text response to display |
thinking |
string | LLM reasoning trace (optional) |
tool_calls |
array | Backend tool executions (optional) |
actions |
array | Suggested UI actions to execute |
Error Response (500):
{
"success": false,
"error": "Failed to get response from assistant"
}Stream a response from the LLM assistant using Server-Sent Events.
Request: Same as Chat with Assistant.
Response: Content-Type: text/event-stream
SSE Event Format:
event: content
data: {"text": "I'll load the..."}
event: thinking
data: {"text": "The user wants..."}
event: tool_call
data: {"name": "load_model", "parameters": {...}}
event: done
data: {}
event: error
data: {"error": "Something went wrong"}
Get conversation history with the assistant.
Response (200):
{
"messages": [
{
"role": "user",
"content": "Load SDXL",
"timestamp": 1704067200
},
{
"role": "assistant",
"content": "I'll load SDXL...",
"timestamp": 1704067201
}
],
"count": 2
}Clear conversation history.
Response (200):
{
"success": true
}Get assistant connection status.
Response (200):
{
"enabled": true,
"connected": true,
"base_url": "http://localhost:11434",
"model": "llama3.2",
"timeout_seconds": 120,
"history_count": 15
}Get assistant configuration.
Response (200):
{
"enabled": true,
"endpoint": "http://localhost:11434/v1",
"model": "llama3.2",
"temperature": 0.7,
"max_tokens": 4096,
"timeout_seconds": 120,
"max_history_turns": 50,
"proactive_suggestions": true,
"system_prompt": "You are a helpful assistant...",
"default_system_prompt": "You are a helpful assistant...",
"has_api_key": true
}| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether the assistant is enabled |
endpoint |
string | LLM API endpoint URL |
model |
string | LLM model name |
temperature |
float | Sampling temperature |
max_tokens |
integer | Maximum response tokens |
timeout_seconds |
integer | Request timeout |
max_history_turns |
integer | Maximum conversation turns to keep |
proactive_suggestions |
boolean | Whether to offer proactive suggestions |
system_prompt |
string | Current system prompt |
default_system_prompt |
string | Default system prompt |
has_api_key |
boolean | Whether an API key is configured |
Update assistant configuration at runtime.
Request:
{
"enabled": true,
"endpoint": "http://localhost:11434/v1",
"model": "llama3.2",
"temperature": 0.7,
"max_tokens": 4096,
"timeout_seconds": 120,
"max_history_turns": 50,
"proactive_suggestions": true,
"system_prompt": "Custom system prompt",
"api_key": "optional_key"
}Response (200):
{
"success": true,
"settings": {
"enabled": true,
"endpoint": "http://localhost:11434/v1",
"model": "llama3.2",
"temperature": 0.7,
"max_tokens": 4096,
"timeout_seconds": 120,
"max_history_turns": 50,
"proactive_suggestions": true,
"system_prompt": "Custom system prompt",
"default_system_prompt": "You are a helpful assistant...",
"has_api_key": true
}
}Get information about the assistant's LLM model capabilities.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | No | Model name to query (defaults to current model) |
Response (200):
{
"model": "llama3.2",
"capabilities": ["chat", "tool_use"],
"context_length": 131072,
"family": "llama",
"parameter_size": "3B",
"has_vision": false
}Get all supported model architecture presets and current architecture info.
Response (200):
{
"architectures": {
"SD1": {
"id": "SD1",
"name": "Stable Diffusion 1.5",
"description": "Standard SD 1.x models",
"aliases": ["SD1.5", "SD1.x"],
"requiredComponents": {
"clip": "CLIP text encoder"
},
"optionalComponents": {
"vae": "VAE model",
"lora": "LoRA weights",
"controlnet": "ControlNet model"
},
"loadOptions": {
"flash_attn": true
},
"generationDefaults": {
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0,
"sampler": "euler_a",
"scheduler": "discrete"
}
},
"Flux": {
"id": "Flux",
"name": "Flux",
"description": "Black Forest Labs Flux models",
"aliases": [],
"requiredComponents": {
"clip_l": "CLIP-L text encoder",
"t5xxl": "T5-XXL text encoder",
"vae": "VAE (ae.safetensors)"
},
"optionalComponents": {
"lora": "LoRA weights"
},
"loadOptions": {
"flash_attn": true,
"backend": "te=cpu"
},
"generationDefaults": {
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 1.0,
"sampler": "euler"
},
"imageEditMode": "ref_images"
}
},
"current_architecture": "Flux",
"current_preset": { ... }
}| Field | Type | Description |
|---|---|---|
architectures |
object | Map of architecture ID to preset |
architectures.*.id |
string | Architecture identifier |
architectures.*.name |
string | Display name |
architectures.*.description |
string | Architecture description |
architectures.*.aliases |
array | Alternative names |
architectures.*.requiredComponents |
object | Required component models |
architectures.*.optionalComponents |
object | Optional component models |
architectures.*.loadOptions |
object | Recommended load options |
architectures.*.generationDefaults |
object | Default generation parameters |
architectures.*.imageEditMode |
string|null | Image edit mode (e.g., "ref_images") |
current_architecture |
string|null | Currently loaded model's architecture |
current_preset |
object|null | Full preset for current architecture |
Detect the architecture of a model without loading it.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model name to detect |
Response (detected):
{
"detected": true,
"architecture": {
"id": "Flux",
"name": "Flux",
"description": "Black Forest Labs Flux models",
"aliases": [],
"requiredComponents": { ... },
"optionalComponents": { ... },
"loadOptions": { ... },
"generationDefaults": { ... },
"imageEditMode": "ref_images"
}
}Response (not detected):
{
"detected": false,
"architecture": null
}Endpoints for downloading models from external sources.
Download a model from URL, CivitAI, or HuggingFace.
Request:
{
"model_type": "checkpoint",
"source": "civitai",
"model_id": "123456",
"url": "https://...",
"repo_id": "org/repo",
"filename": "model.safetensors",
"subfolder": "anime",
"revision": "main"
}| Field | Type | Required | Description |
|---|---|---|---|
model_type |
string | Yes | Target model type (checkpoint, diffusion, vae, lora, clip, t5, embedding, controlnet, llm, esrgan, taesd) |
source |
string | No | Source type: url, civitai, huggingface (auto-detected from other fields) |
model_id |
string | For CivitAI | CivitAI model ID (format: "123456" or "123456:789012" for version) |
url |
string | For URL | Direct download URL (also accepts CivitAI/HuggingFace URLs for auto-detection) |
repo_id |
string | For HuggingFace | HuggingFace repository ID (e.g., "stabilityai/sdxl-turbo") |
filename |
string | For HuggingFace | Filename in repository |
subfolder |
string | No | Target subfolder in model directory |
revision |
string | No | Git revision for HuggingFace (default: "main") |
Source Auto-Detection:
If source is not specified:
- URL containing
civitai.com→civitai - URL containing
huggingface.co→huggingface urlpresent →urlmodel_idpresent →civitairepo_idandfilenamepresent →huggingface
Response (202 Accepted):
{
"success": true,
"download_job_id": "550e8400-e29b-41d4-a716-446655440000",
"hash_job_id": "660e8400-e29b-41d4-a716-446655440001",
"source": "civitai",
"model_type": "checkpoint",
"position": 1
}Get metadata for a CivitAI model.
Path Parameters:
id: CivitAI model ID. Supports format123456or123456:789012(model:version)
Response (200):
{
"success": true,
"model_id": 123456,
"version_id": 789012,
"name": "Model Name",
"version_name": "v1.0",
"type": "Checkpoint",
"base_model": "SDXL 1.0",
"filename": "model.safetensors",
"file_size": 5368709120,
"sha256": "abc123...",
"download_url": "https://civitai.com/..."
}Get metadata for a HuggingFace model file.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
repo_id |
string | Yes | HuggingFace repository ID (e.g., "stabilityai/sdxl-turbo") |
filename |
string | Yes | Filename within repository |
revision |
string | No | Git revision (default: "main") |
Response (200):
{
"success": true,
"repo_id": "stabilityai/sdxl-turbo",
"filename": "sd_xl_turbo_1.0.safetensors",
"revision": "main",
"file_size": 5368709120,
"download_url": "https://huggingface.co/..."
}Get configured model storage paths.
Response (200):
{
"checkpoints": "/path/to/checkpoints",
"diffusion_models": "/path/to/diffusion_models",
"vae": "/path/to/vae",
"lora": "/path/to/loras",
"clip": "/path/to/clip",
"t5": "/path/to/t5xxl",
"embeddings": "/path/to/embeddings",
"controlnet": "/path/to/controlnet",
"llm": "/path/to/llm",
"esrgan": "/path/to/esrgan",
"taesd": "/path/to/taesd"
}Real-time progress updates are available via WebSocket connection on a separate port (default: 8081, see ws_port in health response).
Connect to ws://<host>:<ws_port> (e.g., ws://localhost:8081).
All messages are JSON with this structure:
{
"event": "event_type",
"timestamp": "2024-01-01T12:00:00.000Z",
"data": { ... }
}| Event | Description |
|---|---|
job_added |
New job added to queue |
job_status_changed |
Job status changed (pending/processing/completed/failed/cancelled) |
job_progress |
Generation progress update (step/total_steps) |
job_preview |
Live preview image during generation |
job_cancelled |
Job was cancelled |
| Event | Description |
|---|---|
model_loading_progress |
Model loading progress |
model_loaded |
Model finished loading |
model_load_failed |
Model failed to load |
model_unloaded |
Model was unloaded |
upscaler_loaded |
Upscaler finished loading |
upscaler_unloaded |
Upscaler was unloaded |
| Event | Description |
|---|---|
server_status |
Periodic heartbeat/status update |
server_shutdown |
Server is shutting down |
Sent during generation when previews are enabled. Contains a base64-encoded JPEG image.
Example:
{
"event": "job_preview",
"timestamp": "2024-01-01T12:00:30.123Z",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"step": 10,
"frame_count": 1,
"width": 256,
"height": 256,
"is_noisy": false,
"image": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
}| Field | Type | Description |
|---|---|---|
job_id |
string | Job UUID |
step |
integer | Current generation step |
frame_count |
integer | Number of frames (1 for images, >1 for video) |
width |
integer | Preview image width |
height |
integer | Preview image height |
is_noisy |
boolean | Whether this is a noisy preview |
image |
string | Base64-encoded JPEG data URL |
Example:
{
"event": "job_progress",
"timestamp": "2024-01-01T12:00:25.456Z",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"step": 10,
"total_steps": 20
}
}Example:
{
"event": "job_status_changed",
"timestamp": "2024-01-01T12:01:00.789Z",
"data": {
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"previous_status": "processing",
"outputs": ["550e8400-e29b-41d4-a716-446655440000/image_0.png"]
}
}const ws = new WebSocket('ws://localhost:8081');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.event) {
case 'job_progress':
console.log(`Progress: ${msg.data.step}/${msg.data.total_steps}`);
break;
case 'job_preview':
// Display preview image
document.getElementById('preview').src = msg.data.image;
break;
case 'job_status_changed':
console.log(`Job ${msg.data.job_id}: ${msg.data.status}`);
break;
}
};All error responses follow this format:
{
"error": "Error message describing what went wrong"
}Common HTTP Status Codes:
| Code | Description |
|---|---|
| 200 | Success |
| 202 | Accepted (job queued) |
| 400 | Bad Request (invalid parameters, no model loaded) |
| 404 | Not Found (model or job not found) |
| 409 | Conflict (e.g. another model is already loading) |
| 500 | Internal Server Error |
| 504 | Gateway Timeout (e.g. ?wait=true exceeded its timeout — work continues in background) |
Endpoints that accept structured input reject unknown JSON body fields and unknown query parameters with 400 Bad Request. This is intentional — silently ignoring typos like diffusion_fa (vs the real field diffusion_flash_attn) used to cause "I sent it but nothing happened" bugs.
Example — unknown body field:
curl -X POST http://localhost:8080/models/load \
-H 'Content-Type: application/json' \
-d '{"model_name":"foo.gguf","model_type":"diffusion","options":{"diffusion_fa":true}}'{
"error": "Unknown field(s) in /models/load options: diffusion_fa. Check the spelling against the OpenAPI schema at /openapi.json."
}Example — unknown query parameter:
curl -X POST 'http://localhost:8080/models/load?waitt=true' ...{
"error": "Unknown query parameter(s): waitt. Accepted: wait, timeout."
}Endpoints that strictly validate today:
| Endpoint | Body keys | Query params |
|---|---|---|
POST /models/load |
top-level + options sub-object |
wait, timeout |
POST /txt2img |
enumerated in Txt2ImgParams::from_json |
n/a |
POST /img2img |
enumerated in Img2ImgParams::from_json |
n/a |
POST /txt2vid |
enumerated in Txt2VidParams::from_json |
n/a |
POST /upscale |
enumerated in UpscaleParams::from_json |
n/a |
POST /convert |
inline (input_path, output_path, output_type, model_type, model_name, vae_path, tensor_type_rules) |
n/a |
GET /queue |
n/a (body-less) | status, type, search, architecture, group_by, limit, offset, page, before, after |
PUT /preview/settings |
inline (enabled, mode, interval, max_size, quality) |
n/a |
PUT /settings/output |
inline (output_group_folders) |
n/a |
Other body-accepting endpoints (/auth/login, /models/upload multipart, settings PUTs for assistant/generation/preferences) currently do not enforce strict-key validation — they're either internal-WebUI-only or simple enough that typos are unlikely to confuse users.
The full closed key list per endpoint is reflected in the auto-generated OpenAPI spec at GET /openapi.json — the schema is the source of truth, the C++ parsers are kept in sync with it.
Type coercion is still tolerant. Strict-key validation is independent from type coercion: "steps": "9" (string-encoded number) is still coerced to 9, because many naive HTTP clients (shell scripts, form-encoded wrappers) emit numbers as strings. The boundary is "the field name must exist", not "the value must be exactly the right JSON type".
| Status | Description |
|---|---|
pending |
Job is waiting in queue |
processing |
Job is currently being processed |
completed |
Job finished successfully |
failed |
Job encountered an error |
cancelled |
Job was cancelled by user |
deleted |
Job is in recycle bin (soft-deleted) |
| Type | Description |
|---|---|
txt2img |
Text to Image |
img2img |
Image to Image |
txt2vid |
Text to Video |
upscale |
Image Upscaling |
convert |
Model Format Conversion |
model_download |
Model Download |
model_hash |
Model Hash Computation |
Available sampling methods:
| Sampler | Description |
|---|---|
euler |
Euler sampler |
euler_a |
Euler Ancestral sampler |
heun |
Heun sampler |
dpm2 |
DPM2 sampler |
dpm++2s_a |
DPM++ 2S Ancestral |
dpm++2m |
DPM++ 2M |
dpm++2mv2 |
DPM++ 2M v2 |
ipndm |
IPNDM sampler |
ipndm_v |
IPNDM V sampler |
lcm |
LCM sampler |
ddim_trailing |
DDIM Trailing sampler |
tcd |
TCD sampler |
res_multistep |
Res Multistep sampler |
res_2s |
Res 2S sampler |
er_sde |
ER-SDE sampler |
euler_cfg_pp |
Euler CFG++ sampler |
euler_a_cfg_pp |
Euler Ancestral CFG++ sampler |
euler_ge |
Euler Gradient Estimation sampler |
Available scheduler types:
| Scheduler | Description |
|---|---|
discrete |
Discrete scheduler (default) |
karras |
Karras scheduler |
exponential |
Exponential scheduler |
ays |
AYS scheduler |
gits |
GITS scheduler |
sgm_uniform |
SGM Uniform scheduler |
simple |
Simple scheduler |
smoothstep |
Smoothstep scheduler (Z-Image) |
kl_optimal |
KL Optimal scheduler |
lcm |
LCM scheduler |
bong_tangent |
Bong Tangent scheduler |
ltx2 |
LTX2 scheduler (default for LTXAV / LTX 2.3 video models) |
| Type | Directory | Description |
|---|---|---|
checkpoint |
checkpoints/ |
SD1.x, SD2.x, SDXL full models |
diffusion |
diffusion_models/ |
Flux, SD3, Wan, Qwen models |
vae |
vae/ |
VAE models |
lora |
loras/ |
LoRA models |
clip |
clip/ |
CLIP text encoders |
t5 |
t5/ |
T5 text encoders |
controlnet |
controlnet/ |
ControlNet models |
llm |
llm/ |
LLM models for multimodal (Qwen, etc.) |
esrgan |
esrgan/ |
ESRGAN upscaler models |
taesd |
taesd/ |
TAESD preview models |
embedding |
embeddings/ |
Textual inversion embeddings |
Load SD1.5 model:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "v1-5-pruned-emaonly.safetensors",
"model_type": "checkpoint"
}'Generate image with SD1.5:
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat",
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0
}'Load SDXL model with separate VAE:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "sd_xl_base_1.0.safetensors",
"model_type": "checkpoint",
"vae": "sdxl_vae-fp16-fix.safetensors"
}'Generate 1024x1024 image with SDXL:
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat, masterpiece, best quality",
"negative_prompt": "low quality, worst quality",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 7.0
}'Load SD3.5 Large with text encoders:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "sd3.5_large.safetensors",
"model_type": "checkpoint",
"clip_l": "clip_l.safetensors",
"clip_g": "clip_g.safetensors",
"t5xxl": "t5xxl_fp16.safetensors",
"options": {
"backend": "te=cpu"
}
}'Generate with SD3.5:
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 4.5,
"sampler": "euler"
}'Load Flux-dev (quantized GGUF):
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "flux1-dev-Q4_K_S.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"clip_l": "clip_l.safetensors",
"t5xxl": "t5xxl_fp16.safetensors",
"options": {
"backend": "te=cpu",
"flash_attn": true
}
}'Generate with Flux (cfg_scale=1.0 recommended):
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat holding a sign says \"flux.cpp\"",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 1.0,
"sampler": "euler"
}'Load Flux-schnell (faster, fewer steps):
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "flux1-schnell-Q8_0.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"clip_l": "clip_l.safetensors",
"t5xxl": "t5xxl_fp16.safetensors",
"options": {
"backend": "te=cpu"
}
}'Generate with Flux-schnell (4 steps):
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat holding a sign says \"flux.cpp\"",
"width": 1024,
"height": 1024,
"steps": 4,
"cfg_scale": 1.0,
"sampler": "euler"
}'Load Qwen-Image with LLM text encoder:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "qwen-image-Q8_0.gguf",
"model_type": "diffusion",
"vae": "qwen_image_vae.safetensors",
"llm": "Qwen2.5-VL-7B-Instruct-Q8_0.gguf",
"options": {
"params_backend": "*=cpu",
"flash_attn": true
}
}'Generate with Qwen-Image:
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "A beautiful Chinese woman wearing a T-shirt with the QWEN logo is smiling at the camera",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 2.5,
"sampler": "euler"
}'Z-Image is a fast image generation model that uses Qwen3 as text encoder and can run on GPUs with 4GB VRAM or less.
Load Z-Image Turbo:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "z_image_turbo-Q8_0.gguf",
"model_type": "diffusion",
"vae": "ae_q8_0.gguf",
"llm": "qwen_3_4b.Q8_0.gguf",
"options": {
"flash_attn": true,
"vae_conv_direct": true,
"weight_type": "q5_0"
}
}'Generate with Z-Image Turbo (8 steps, cfg_scale=1, smoothstep scheduler, easycache):
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "A solitary 27-year-old woman sits sideways on a weathered tree trunk",
"width": 1024,
"height": 688,
"steps": 8,
"cfg_scale": 1.0,
"sampler": "euler",
"scheduler": "smoothstep",
"seed": 42,
"batch_count": 4,
"easycache": true
}'Load Wan2.1 T2V 1.3B:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "wan2.1_t2v_1.3B_fp16.safetensors",
"model_type": "diffusion",
"vae": "wan_2.1_vae.safetensors",
"t5xxl": "umt5-xxl-encoder-Q8_0.gguf",
"options": {
"flash_attn": true,
"flow_shift": 3.0
}
}'Generate video with Wan2.1:
curl -X POST http://localhost:8080/txt2vid \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat walking in a garden",
"negative_prompt": "static, blurry, low quality",
"width": 832,
"height": 480,
"video_frames": 33,
"steps": 30,
"cfg_scale": 6.0,
"sampler": "euler",
"flow_shift": 3.0
}'LoRAs are specified directly in the prompt using the syntax <lora:name:weight>:
Generate with LoRA (Flux example):
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat <lora:realism_lora:1.0>",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 1.0,
"sampler": "euler"
}'Multiple LoRAs:
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a beautiful landscape <lora:add_detail:0.8> <lora:enhance_colors:0.5>",
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0
}'Load model with ControlNet:
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "v1-5-pruned-emaonly.safetensors",
"model_type": "checkpoint",
"controlnet": "control_v11p_sd15_canny.safetensors"
}'Generate with control image (base64 encoded pre-processed canny edge image):
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a beautiful house, detailed architecture",
"control_image_base64": "'$(base64 -w0 canny_edges.png)'",
"control_strength": 0.9,
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0
}'Transform an existing image:
curl -X POST http://localhost:8080/img2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "cat with blue eyes, masterpiece",
"init_image_base64": "'$(base64 -w0 input.png)'",
"strength": 0.4,
"width": 512,
"height": 512,
"steps": 20,
"cfg_scale": 7.0
}'List all available models:
curl http://localhost:8080/modelsFilter models by type:
curl "http://localhost:8080/models?type=diffusion"Search models by name:
curl "http://localhost:8080/models?search=flux"Check server health:
curl http://localhost:8080/healthCheck job status:
curl http://localhost:8080/queue/{job_id}List all jobs:
curl http://localhost:8080/queueList jobs with filters:
curl "http://localhost:8080/queue?status=completed&type=txt2img&limit=10"Cancel a pending job:
curl -X DELETE http://localhost:8080/queue/{job_id}Download generated image:
curl -O http://localhost:8080/output/{job_id}/image_0.pngUnload current model:
curl -X POST http://localhost:8080/models/unloadCompute model hash:
curl http://localhost:8080/models/hash/checkpoint/v1-5-pruned-emaonly.safetensorsimport requests
import time
import base64
BASE_URL = "http://localhost:8080"
# Load Flux model
resp = requests.post(f"{BASE_URL}/models/load", json={
"model_name": "flux1-dev-Q4_K_S.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"clip_l": "clip_l.safetensors",
"t5xxl": "t5xxl_fp16.safetensors",
"options": {
"backend": "te=cpu",
"flash_attn": True
}
})
print(resp.json())
# Generate image
resp = requests.post(f"{BASE_URL}/txt2img", json={
"prompt": "a cute cat wearing a hat",
"width": 1024,
"height": 1024,
"steps": 20,
"cfg_scale": 1.0,
"sampler": "euler"
})
job = resp.json()
job_id = job["job_id"]
print(f"Job created: {job_id}")
# Poll for completion
while True:
resp = requests.get(f"{BASE_URL}/queue/{job_id}")
status = resp.json()
prog = status['progress']
print(f"Status: {status['status']}, Step {prog['step']}/{prog['total_steps']}")
if status["status"] in ["completed", "failed", "cancelled"]:
break
time.sleep(1)
# Download result
if status["status"] == "completed":
for output in status["outputs"]:
img_resp = requests.get(f"{BASE_URL}/output/{output}")
with open(output.split("/")[-1], "wb") as f:
f.write(img_resp.content)
print(f"Saved: {output}")#!/bin/bash
# Complete workflow: load model, generate image, download result
BASE_URL="http://localhost:8080"
# 1. Load model
echo "Loading model..."
curl -s -X POST "$BASE_URL/models/load" \
-H "Content-Type: application/json" \
-d '{
"model_name": "v1-5-pruned-emaonly.safetensors",
"model_type": "checkpoint"
}'
# 2. Submit generation job
echo -e "\n\nSubmitting job..."
RESPONSE=$(curl -s -X POST "$BASE_URL/txt2img" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a lovely cat sitting on a windowsill",
"width": 512,
"height": 512,
"steps": 20
}')
JOB_ID=$(echo $RESPONSE | jq -r '.job_id')
echo "Job ID: $JOB_ID"
# 3. Poll for completion
echo "Waiting for completion..."
while true; do
STATUS=$(curl -s "$BASE_URL/queue/$JOB_ID")
STATE=$(echo $STATUS | jq -r '.status')
STEP=$(echo $STATUS | jq -r '.progress.step')
TOTAL=$(echo $STATUS | jq -r '.progress.total_steps')
echo "Status: $STATE, Step $STEP/$TOTAL"
if [ "$STATE" = "completed" ] || [ "$STATE" = "failed" ]; then
break
fi
sleep 2
done
# 4. Download result
if [ "$STATE" = "completed" ]; then
OUTPUT=$(echo $STATUS | jq -r '.outputs[0]')
echo "Downloading: $OUTPUT"
curl -s -O "$BASE_URL/output/$OUTPUT"
echo "Saved: $(basename $OUTPUT)"
fi