> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uncensored.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Studio — Edit

> Edit existing images with a text prompt and reference image(s).

export const ModelCards = () => {
  const COPY_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
  const CHECK_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>';
  const SEARCH_ICON = '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
  useEffect(() => {
    let cancelled = false;
    const findCol = (headers, name) => headers.findIndex(h => h === name.toLowerCase());
    const buildCard = (cells, map) => {
      const cell = i => i > -1 && cells[i] ? cells[i] : null;
      const txt = i => cell(i) ? cell(i).textContent.trim() : "";
      const html = i => cell(i) ? cell(i).innerHTML.trim() : "";
      const id = txt(map.id);
      const name = map.name > -1 ? txt(map.name) : id;
      const card = document.createElement("div");
      card.className = "mc-card";
      card.dataset.search = (id + " " + name + " " + txt(map.type)).toLowerCase();
      const metaBits = [];
      if (map.status > -1 && html(map.status)) metaBits.push('<span class="mc-status">' + html(map.status) + "</span>");
      if (map.type > -1 && txt(map.type)) metaBits.push('<span class="mc-tag">' + txt(map.type) + "</span>");
      if (map.cost > -1 && html(map.cost)) metaBits.push('<span class="mc-cost">' + html(map.cost) + "</span>");
      if (map.eta > -1 && txt(map.eta)) metaBits.push('<span class="mc-eta">' + txt(map.eta) + "</span>");
      const params = [];
      if (map.req > -1 && html(map.req)) params.push('<div class="mc-param"><span class="mc-plabel">Required</span><span class="mc-pval">' + html(map.req) + "</span></div>");
      if (map.opt > -1 && html(map.opt)) params.push('<div class="mc-param"><span class="mc-plabel">Optional</span><span class="mc-pval">' + html(map.opt) + "</span></div>");
      card.innerHTML = '<div class="mc-head">' + '<div class="mc-left">' + '<div class="mc-title">' + name + "</div>" + '<div class="mc-idrow"><code class="mc-id">' + id + '</code><button type="button" class="mc-copy" title="Copy model ID" aria-label="Copy model ID ' + id + '">' + COPY_ICON + "</button></div>" + "</div>" + (metaBits.length ? '<div class="mc-meta">' + metaBits.join("") + "</div>" : "") + "</div>" + (params.length ? '<div class="mc-params">' + params.join("") + "</div>" : "");
      const btn = card.querySelector(".mc-copy");
      if (btn) {
        btn.addEventListener("click", e => {
          e.preventDefault();
          e.stopPropagation();
          navigator.clipboard.writeText(id).then(() => {
            btn.innerHTML = CHECK_ICON;
            btn.classList.add("copied");
            setTimeout(() => {
              btn.innerHTML = COPY_ICON;
              btn.classList.remove("copied");
            }, 1200);
          });
        });
      }
      return card;
    };
    const enhance = () => {
      if (cancelled) return;
      document.querySelectorAll("table").forEach(table => {
        if (table.dataset.cardsEnhanced) return;
        const headers = Array.from(table.querySelectorAll("thead th")).map(th => th.textContent.trim().toLowerCase());
        if (headers[0] !== "model id") return;
        table.dataset.cardsEnhanced = "true";
        const map = {
          id: 0,
          status: findCol(headers, "status"),
          name: findCol(headers, "display name"),
          type: findCol(headers, "type"),
          cost: findCol(headers, "cost"),
          eta: findCol(headers, "eta"),
          req: findCol(headers, "required params"),
          opt: findCol(headers, "optional supported params")
        };
        const rows = Array.from(table.querySelectorAll("tbody tr"));
        const wrap = document.createElement("div");
        wrap.className = "mc-wrap";
        const list = document.createElement("div");
        list.className = "mc-list";
        rows.forEach(tr => list.appendChild(buildCard(Array.from(tr.children), map)));
        const count = document.createElement("div");
        count.className = "mc-count";
        const setCount = n => count.textContent = n + (n === 1 ? " model" : " models");
        setCount(rows.length);
        const empty = document.createElement("div");
        empty.className = "mc-empty";
        empty.textContent = "No models match your search.";
        empty.style.display = "none";
        if (rows.length >= 6) {
          const box = document.createElement("div");
          box.className = "mc-search-box";
          box.innerHTML = SEARCH_ICON;
          const input = document.createElement("input");
          input.type = "text";
          input.className = "mc-search";
          input.placeholder = "Search models…";
          box.appendChild(input);
          input.addEventListener("input", () => {
            const q = input.value.trim().toLowerCase();
            let n = 0;
            list.querySelectorAll(".mc-card").forEach(c => {
              const show = !q || c.dataset.search.indexOf(q) !== -1;
              c.style.display = show ? "" : "none";
              if (show) n++;
            });
            setCount(n);
            empty.style.display = n ? "none" : "";
          });
          wrap.appendChild(box);
        }
        wrap.appendChild(count);
        wrap.appendChild(list);
        wrap.appendChild(empty);
        table.insertAdjacentElement("beforebegin", wrap);
        table.style.display = "none";
      });
    };
    enhance();
    const raf = requestAnimationFrame(enhance);
    const t = setTimeout(enhance, 300);
    return () => {
      cancelled = true;
      cancelAnimationFrame(raf);
      clearTimeout(t);
    };
  }, []);
  const css = `
    .mc-wrap {
      --mc-fg: #1a1a1a;
      --mc-muted: #6b7280;
      --mc-field-bg: transparent;
      --mc-field-border: rgba(0,0,0,0.14);
      --mc-field-border-hover: rgba(0,0,0,0.30);
      --mc-focus-ring: rgba(0,0,0,0.07);
      --mc-code-bg: rgba(0,0,0,0.045);
      --mc-divider: rgba(0,0,0,0.08);
      --mc-row-hover: rgba(0,0,0,0.018);
      margin: 1rem 0 1.5rem;
    }
    :is(.dark) .mc-wrap {
      --mc-fg: #f3f4f6;
      --mc-muted: #9ca3af;
      --mc-field-border: rgba(255,255,255,0.16);
      --mc-field-border-hover: rgba(255,255,255,0.36);
      --mc-focus-ring: rgba(255,255,255,0.09);
      --mc-code-bg: rgba(255,255,255,0.07);
      --mc-divider: rgba(255,255,255,0.08);
      --mc-row-hover: rgba(255,255,255,0.025);
    }

    .mc-search-box { position: relative; display: flex; align-items: center; }
    .mc-search-box svg { position: absolute; left: 14px; color: var(--mc-muted); pointer-events: none; }
    .mc-search {
      width: 100%; box-sizing: border-box;
      padding: 11px 14px 11px 42px; font-size: 15px;
      color: var(--mc-fg); background: var(--mc-field-bg);
      border: 1px solid var(--mc-field-border); border-radius: 10px; outline: none;
      transition: border-color .12s ease, box-shadow .12s ease;
    }
    .mc-search::placeholder { color: var(--mc-muted); }
    .mc-search:hover { border-color: var(--mc-field-border-hover); }
    .mc-search:focus { border-color: var(--mc-field-border-hover); box-shadow: 0 0 0 3px var(--mc-focus-ring); }

    .mc-count { font-size: 13px; color: var(--mc-muted); margin: 16px 2px 2px; }
    .mc-list { display: flex; flex-direction: column; }

    /* Borderless rows separated only by a thin divider */
    .mc-card {
      padding: 18px 8px;
      border-bottom: 1px solid var(--mc-divider);
      border-radius: 8px;
      transition: background .12s ease;
    }
    .mc-card:last-child { border-bottom: none; }
    .mc-card:hover { background: var(--mc-row-hover); }

    .mc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
    .mc-left { min-width: 0; }
    .mc-title { font-size: 15px; font-weight: 600; color: var(--mc-fg); line-height: 1.3; }
    .mc-idrow { display: inline-flex; align-items: center; gap: 7px; margin-top: 7px; }
    .mc-id {
      font-size: 12.5px; font-weight: 500;
      padding: 2px 7px; border-radius: 6px;
      background: var(--mc-code-bg); color: var(--mc-fg);
      word-break: break-all;
    }
    .mc-copy {
      display: inline-flex; align-items: center; justify-content: center;
      padding: 2px; color: var(--mc-muted);
      background: transparent; border: none; border-radius: 4px;
      cursor: pointer; opacity: 0; transition: opacity .12s ease, color .12s ease;
    }
    .mc-card:hover .mc-copy { opacity: .7; }
    .mc-copy:hover { opacity: 1; color: var(--mc-fg); }
    .mc-copy.copied { color: #16a34a; opacity: 1; }

    .mc-meta { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px 14px; flex-shrink: 0; text-align: right; }
    .mc-meta code { background: transparent; padding: 0; font-size: 13px; color: var(--mc-fg); }
    .mc-status :is(span, .badge) { vertical-align: middle; }
    .mc-tag { font-size: 12px; color: var(--mc-muted); }
    .mc-cost { font-size: 13px; font-weight: 600; color: var(--mc-fg); }
    .mc-eta { font-size: 12.5px; color: var(--mc-muted); }

    /* Params shown openly (documentation, not hidden behind a toggle) */
    .mc-params { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
    .mc-param { display: grid; grid-template-columns: 84px 1fr; gap: 12px; align-items: start; }
    .mc-plabel { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--mc-muted); padding-top: 2px; }
    .mc-pval { font-size: 13.5px; color: var(--mc-fg); line-height: 1.6; }
    .mc-pval code { font-size: 12px; padding: 1px 5px; border-radius: 5px; background: var(--mc-code-bg); }

    .mc-empty { padding: 24px 8px; font-size: 14px; color: var(--mc-muted); }

    @media (max-width: 600px) {
      .mc-card { padding: 16px 0; }
      .mc-count, .mc-empty { margin-left: 0; padding-left: 0; }
      .mc-search { font-size: 16px; } /* avoid iOS focus zoom */

      .mc-head { flex-direction: column; gap: 0; }
      .mc-idrow { margin-top: 8px; }
      /* Meta becomes a left-aligned inline group under the ID */
      .mc-meta { justify-content: flex-start; text-align: left; margin-top: 11px; gap: 6px 14px; }
      .mc-copy { opacity: .7; padding: 5px; margin: -3px; } /* larger tap target */

      /* Params stack label-over-value, full width */
      .mc-params { margin-top: 13px; gap: 11px; }
      .mc-param { grid-template-columns: 1fr; gap: 3px; }
      .mc-plabel { padding-top: 0; }
    }
  `;
  return <style dangerouslySetInnerHTML={{
    __html: css
  }} />;
};

<ModelCards />

# Image Studio — Edit <Badge color="green" shape="pill">Live</Badge>

```text theme={null}
POST /v1/images/studio/edit
```

Edit one or more input images with a text instruction. The endpoint is always async — it accepts your job and returns immediately with a `job_id`. Retrieve the result by polling the `status_url` or by supplying a `callback_url` to receive a webhook when the job completes.

## Request body

| Parameter             | Type             | Required | Description                                                                                                                                                         |
| --------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`              | string           | Yes      | Editing instructions. 1-2,000 characters.                                                                                                                           |
| `image_urls`          | array of strings | Yes      | 1-5 reference image URLs to edit. Some models use only the first image — see the [model catalog](/models#image-studio).                                             |
| `model`               | string           | Yes      | Edit model to use. The API defaults to `standard` if omitted, but integrations should send this explicitly. Full list in the [model catalog](/models#image-studio). |
| `callback_url`        | string           | No       | Webhook URL that will receive the final result.                                                                                                                     |
| `additional_settings` | object           | No       | Model-specific settings (e.g., `resolution`, `quality`, `aspect_ratio`). Refer to the [model catalog](/models#image-studio) for accepted values per model.          |
| `enable_fallback`     | boolean          | No       | Defaults to `true`. Accepted for forward compatibility; image edit fallback behavior is model-dependent.                                                            |

## Model support

Default model: `standard`. The API can default `model` when omitted, but production integrations should send it explicitly so the selected edit model is clear.

`common` means `callback_url`, `additional_settings`, and `enable_fallback`. Model-specific knobs are passed in `additional_settings`.

| Model ID                             | Display name                     | Required params                 | Optional supported params                                                                                                                                       | Cost                                                 | ETA   |
| ------------------------------------ | -------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ----- |
| `standard`                           | Gemini 2.5 Flash                 | `prompt`, `image_urls`, `model` | common; `image_urls` 1-5                                                                                                                                        | `$0.10`                                              | \~8s  |
| `pro`                                | Gemini 3 Pro                     | `prompt`, `image_urls`, `model` | common; `image_urls` 1-5                                                                                                                                        | `$0.20`                                              | \~10s |
| `qwen-image/edit-2511`               | Qwen Image Edit 2511             | `prompt`, `image_urls`, `model` | common; `image_urls` 1-3                                                                                                                                        | `$0.10`                                              | \~15s |
| `Seedream-4.5-uncensored`            | Seedream v4.5                    | `prompt`, `image_urls`, `model` | common + `seed`; `image_urls` 1-5                                                                                                                               | `$0.10`                                              | \~15s |
| `Wan-2.6`                            | Wan 2.6                          | `prompt`, `image_urls`, `model` | common + `seed`, `negative_prompt`; `image_urls` 1-5                                                                                                            | `$0.10`                                              | \~20s |
| `Wan-2.6-image-edit`                 | Wan 2.6 Image Edit               | `prompt`, `image_urls`, `model` | common + `seed`, `negative_prompt`; `image_urls` 1-5                                                                                                            | `$0.10`                                              | \~15s |
| `group-edit`                         | Qwen Group Photo                 | `prompt`, `image_urls`, `model` | common + `seed`; `image_urls` 1-5                                                                                                                               | `$0.10`                                              | \~20s |
| `xai/grok-imagine-image/edit`        | Grok Imagine Edit                | `prompt`, `image_urls`, `model` | common + `seed`; provider uses the first image                                                                                                                  | `$0.10`                                              | \~15s |
| `seedream-v5`                        | Seedream v5 Lite                 | `prompt`, `image_urls`, `model` | common; `image_urls` 1-5                                                                                                                                        | `$0.10`                                              | \~15s |
| `nano-banana-2/edit`                 | Nano Banana 2 Edit               | `prompt`, `image_urls`, `model` | common + `resolution` (`0.5k`, `1k`, `2k`, `4k`), `aspect_ratio`, `output_format` (`png`, `jpeg`), `enable_web_search`, `enable_image_search`; `image_urls` 1-5 | `0.5k/1k/2k $0.10`, `4k $0.20`                       | \~20s |
| `nano-banana-2/edit-fast`            | Nano Banana 2 Edit Fast          | `prompt`, `image_urls`, `model` | common + `resolution` (`2k`, `4k`), `aspect_ratio`, `output_format` (`png`, `jpeg`); `image_urls` 1-5                                                           | `$0.10`                                              | \~12s |
| `seedream-v4.5/edit-sequential`      | Seedream v4.5 Edit Sequential    | `prompt`, `image_urls`, `model` | common + `seed`, `max_images` (1-15); `image_urls` 1-5                                                                                                          | `$0.10`                                              | \~25s |
| `seedream-v5.0-lite/edit-sequential` | Seedream v5 Lite Edit Sequential | `prompt`, `image_urls`, `model` | common + `max_images` (1-15); `image_urls` 1-5                                                                                                                  | `$0.10`                                              | \~25s |
| `wan-2.7-image-edit`                 | Wan 2.7 Image Edit               | `prompt`, `image_urls`, `model` | common + `seed`; `image_urls` 1-5                                                                                                                               | `$0.10`                                              | \~15s |
| `wan-2.7-image-edit-pro`             | Wan 2.7 Image Edit Pro           | `prompt`, `image_urls`, `model` | common + `seed`; `image_urls` 1-5                                                                                                                               | `$0.10`                                              | \~20s |
| `gpt-image-2/edit`                   | GPT Image 2 Edit                 | `prompt`, `image_urls`, `model` | common + `quality` (`low`, `medium`, `high`), `resolution` (`1k`, `2k`), `aspect_ratio`, `output_format` (`png`, `jpeg`, `webp`); `image_urls` 1-5              | `low/medium $0.10`, `high 1k $0.30`, `high 2k $0.40` | \~20s |

## Example request

```bash theme={null}
curl --request POST "https://api.uncensored.com/api/v1/images/studio/edit" \
  --header "x-api-key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "prompt": "Add a vibrant sunset background and warm lighting",
    "image_urls": ["https://example.com/photo.jpg"],
    "model": "pro",
    "callback_url": "https://your-server.com/webhook"
  }'
```

## Submit response

```json theme={null}
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "status_url": "https://api.uncensored.com/api/v1/images/studio/edit/jobs/550e8400-e29b-41d4-a716-446655440000",
  "mode": "edit",
  "webhook_url": "https://your-server.com/webhook",
  "created_at": "2026-05-17T10:30:00Z"
}
```

## Get job status

```text theme={null}
GET /v1/images/studio/edit/jobs/{job_id}
```

Poll every 2-5 seconds. Most jobs complete in 10-90 seconds depending on the model.

```json theme={null}
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "image_url": "https://media.lesscensored.com/images/abc123.png",
  "model": "pro",
  "is_fallback": false,
  "error": null,
  "created_at": "2026-05-17T10:30:00Z",
  "completed_at": "2026-05-17T10:30:15Z",
  "processing_time": 15.2
}
```

### Response fields

| Field             | Description                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `job_id`          | ID of the job.                                                                                                                       |
| `status`          | `processing`, `completed`, or `failed`.                                                                                              |
| `image_url`       | URL of the edited image when `status` is `completed`.                                                                                |
| `model`           | The model that produced the result.                                                                                                  |
| `is_fallback`     | `true` if the requested model was temporarily unavailable and a substitute produced the result. The `model` field reports which one. |
| `error`           | Error message when `status` is `failed`; otherwise `null`.                                                                           |
| `processing_time` | Total time in seconds the job took to complete.                                                                                      |

## Webhook payload

If you supplied `callback_url`, the same JSON shape as the status response is POSTed to your webhook once the job reaches a terminal state (`completed` or `failed`).

## Multi-image edits

Pass up to 5 image URLs in `image_urls`. The first image is typically the primary subject; additional images are treated as references. Not every model supports multi-image — the [model catalog](/models#image-studio) marks which models do.

## Variable pricing

Models such as `nano-banana-2/edit` and `gpt-image-2/edit` price the request based on `resolution` and/or `quality`. Pass these in `additional_settings`:

```json theme={null}
{
  "prompt": "Replace the background with a snowy mountain scene",
  "image_urls": ["https://example.com/photo.jpg"],
  "model": "gpt-image-2/edit",
  "additional_settings": {
    "quality": "medium",
    "resolution": "2k",
    "aspect_ratio": "16:9"
  }
}
```

See the [model catalog](/models#image-studio) for each model's accepted values.

## Status codes

| Status code | Description                                                                    |
| ----------- | ------------------------------------------------------------------------------ |
| `202`       | Job accepted and queued.                                                       |
| `400`       | Invalid request (e.g., missing `image_urls`, unknown `model`).                 |
| `402`       | Insufficient funds.                                                            |
| `403`       | Content moderation failed, or your API key's scope does not permit this model. |
| `429`       | Rate limit exceeded.                                                           |
| `500`       | Internal server error.                                                         |
