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

# FLUX Outpainting

> Expand any image beyond its borders in a single call.

export const OutpaintingPlacementDemo = ({canvasWidth = 1600, canvasHeight = 900, refWidth = 720, refHeight = 480}) => {
  const initX = Math.round((canvasWidth - refWidth) / 2);
  const initY = Math.round((canvasHeight - refHeight) / 2);
  const [pos, setPos] = useState({
    x: initX,
    y: initY
  });
  const [grabbing, setGrabbing] = useState(false);
  const svgRef = useRef(null);
  const PAD_L = 110;
  const PAD_T = 60;
  const PAD_R = 60;
  const PAD_B = 110;
  const VBW = canvasWidth + PAD_L + PAD_R;
  const VBH = canvasHeight + PAD_T + PAD_B;
  const CX = PAD_L;
  const CY = PAD_T;
  const RX = CX + pos.x;
  const RY = CY + pos.y;
  const SAGE = "#486A58";
  const SAGE_FAINT = "rgba(72,106,88,0.22)";
  const CREAM = "#F6F1E7";
  const startDrag = e => {
    e.preventDefault();
    e.stopPropagation();
    const svg = svgRef.current;
    if (!svg) return;
    const ctm = svg.getScreenCTM();
    if (!ctm) return;
    const evt = e.touches ? e.touches[0] : e;
    const pt = svg.createSVGPoint();
    pt.x = evt.clientX;
    pt.y = evt.clientY;
    const startSvg = pt.matrixTransform(ctm.inverse());
    const startPos = {
      ...pos
    };
    setGrabbing(true);
    const minX = -Math.round(refWidth * 0.4);
    const minY = -Math.round(refHeight * 0.4);
    const maxX = canvasWidth - Math.round(refWidth * 0.6);
    const maxY = canvasHeight - Math.round(refHeight * 0.6);
    const onMove = ev => {
      ev.preventDefault();
      const me = ev.touches ? ev.touches[0] : ev;
      pt.x = me.clientX;
      pt.y = me.clientY;
      const sp = pt.matrixTransform(ctm.inverse());
      const nx = Math.round(startPos.x + (sp.x - startSvg.x));
      const ny = Math.round(startPos.y + (sp.y - startSvg.y));
      setPos({
        x: Math.max(minX, Math.min(maxX, nx)),
        y: Math.max(minY, Math.min(maxY, ny))
      });
    };
    const onUp = () => {
      setGrabbing(false);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
      window.removeEventListener("touchmove", onMove);
      window.removeEventListener("touchend", onUp);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    window.addEventListener("touchmove", onMove, {
      passive: false
    });
    window.addEventListener("touchend", onUp);
  };
  const reset = () => setPos({
    x: initX,
    y: initY
  });
  const inBounds = pos.x >= 0 && pos.y >= 0 && pos.x + refWidth <= canvasWidth && pos.y + refHeight <= canvasHeight;
  const oyMid = (CY + RY) / 2;
  const oxMid = (CX + RX) / 2;
  return <div className="not-prose" style={{
    width: "100%",
    margin: "1.25rem 0"
  }}>
      <svg ref={svgRef} viewBox={`0 0 ${VBW} ${VBH}`} style={{
    width: "100%",
    height: "auto",
    display: "block",
    userSelect: "none",
    WebkitUserSelect: "none",
    touchAction: "none",
    overflow: "hidden"
  }} xmlns="http://www.w3.org/2000/svg">
        <defs>
          <pattern id="op-hatch" patternUnits="userSpaceOnUse" width="22" height="22" patternTransform="rotate(45)">
            <line x1="0" y1="0" x2="0" y2="22" stroke={SAGE} strokeWidth="2" strokeOpacity="0.16" />
          </pattern>
          <marker id="op-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
            <path d="M0,0 L10,5 L0,10 z" fill={SAGE} />
          </marker>
        </defs>

        {}
        <rect x={CX} y={CY} width={canvasWidth} height={canvasHeight} fill="url(#op-hatch)" stroke={SAGE} strokeWidth="2.5" />

        {}
        <line x1={CX} y1={CY + canvasHeight + 46} x2={CX + canvasWidth} y2={CY + canvasHeight + 46} stroke={SAGE} strokeWidth="1.5" markerStart="url(#op-arrow)" markerEnd="url(#op-arrow)" />
        <text x={CX + canvasWidth / 2} y={CY + canvasHeight + 82} textAnchor="middle" fontFamily="ui-monospace,SFMono-Regular,Menlo,monospace" fontSize="24" fill={SAGE}>
          width = {canvasWidth}
        </text>

        {}
        <line x1={CX - 46} y1={CY} x2={CX - 46} y2={CY + canvasHeight} stroke={SAGE} strokeWidth="1.5" markerStart="url(#op-arrow)" markerEnd="url(#op-arrow)" />
        <text x={CX - 70} y={CY + canvasHeight / 2} textAnchor="middle" fontFamily="ui-monospace,SFMono-Regular,Menlo,monospace" fontSize="24" fill={SAGE} transform={`rotate(-90, ${CX - 70}, ${CY + canvasHeight / 2})`}>
          height = {canvasHeight}
        </text>

        {}
        <line x1={RX + Math.min(refWidth / 2, 24)} y1={CY} x2={RX + Math.min(refWidth / 2, 24)} y2={RY} stroke={SAGE} strokeWidth="1.5" strokeDasharray={pos.y < 0 ? "8,5" : "none"} markerStart="url(#op-arrow)" markerEnd="url(#op-arrow)" />
        <text x={RX + Math.min(refWidth / 2, 24) + 14} y={oyMid + 8} textAnchor="start" fontFamily="ui-monospace,SFMono-Regular,Menlo,monospace" fontSize="22" fill={SAGE}>
          reference_offset_y = {pos.y}
        </text>

        {}
        <line x1={CX} y1={RY + Math.min(refHeight / 2, 24)} x2={RX} y2={RY + Math.min(refHeight / 2, 24)} stroke={SAGE} strokeWidth="1.5" strokeDasharray={pos.x < 0 ? "8,5" : "none"} markerStart="url(#op-arrow)" markerEnd="url(#op-arrow)" />
        <text x={oxMid} y={RY + Math.min(refHeight / 2, 24) + 28} textAnchor="middle" fontFamily="ui-monospace,SFMono-Regular,Menlo,monospace" fontSize="22" fill={SAGE}>
          reference_offset_x = {pos.x}
        </text>

        {}
        <g onMouseDown={startDrag} onTouchStart={startDrag} style={{
    cursor: grabbing ? "grabbing" : "grab"
  }}>
          <rect x={RX} y={RY} width={refWidth} height={refHeight} fill={CREAM} fillOpacity="0.96" stroke={SAGE} strokeWidth="2.5" />
          {}
          <rect x={RX + 6} y={RY + 6} width={refWidth - 12} height={refHeight - 12} fill="none" stroke={SAGE} strokeOpacity="0.25" strokeWidth="1" />
          <text x={RX + refWidth / 2} y={RY + refHeight / 2 + 4} textAnchor="middle" fontFamily="Instrument Sans,system-ui,sans-serif" fontSize="34" fontWeight="500" fill={SAGE}>
            reference image
          </text>
          <text x={RX + refWidth / 2} y={RY + refHeight / 2 + 40} textAnchor="middle" fontFamily="ui-monospace,SFMono-Regular,Menlo,monospace" fontSize="20" fill={SAGE} opacity="0.6">
            {refWidth} × {refHeight}
          </text>
        </g>
      </svg>

      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.75rem 1.25rem",
    alignItems: "center",
    marginTop: "0.5rem",
    fontSize: "0.875rem",
    color: SAGE,
    fontFamily: "Instrument Sans,system-ui,sans-serif"
  }}>
        <span style={{
    opacity: 0.8
  }}>
          Drag the reference image to set its placement on the canvas.
        </span>
        <button onClick={reset} style={{
    padding: "5px 14px",
    borderRadius: "999px",
    border: `1.5px solid ${SAGE}`,
    background: "transparent",
    color: SAGE,
    fontFamily: "inherit",
    fontSize: "0.875rem",
    fontWeight: 500,
    cursor: "pointer",
    lineHeight: 1.2
  }}>
          Reset to center
        </button>
        {!inBounds && <span style={{
    padding: "4px 10px",
    borderRadius: "6px",
    background: SAGE_FAINT,
    fontFamily: "ui-monospace,SFMono-Regular,Menlo,monospace",
    fontSize: "0.8125rem"
  }}>
            out of bounds — set <code>auto_crop: true</code> or expect a <code>422</code>
          </span>}
      </div>
    </div>;
};

export const ImageComparisonSlider = ({beforeImage, afterImage, beforeLabel = "Before", afterLabel = "After", height = "500px", objectFit = "cover", garmentImage, garmentLabel = "Garment reference"}) => {
  const [position, setPosition] = useState(50);
  const dialogRef = useRef(null);
  const openLightbox = () => {
    if (dialogRef.current && typeof dialogRef.current.showModal === "function") {
      dialogRef.current.showModal();
    }
  };
  const closeLightbox = () => {
    if (dialogRef.current && typeof dialogRef.current.close === "function") {
      dialogRef.current.close();
    }
  };
  const getPosition = (e, container) => {
    const rect = container.getBoundingClientRect();
    const clientX = e.touches ? e.touches[0].clientX : e.clientX;
    const x = clientX - rect.left;
    return Math.max(0, Math.min(100, x / rect.width * 100));
  };
  const onPointerDown = e => {
    e.preventDefault();
    e.stopPropagation();
    const container = e.currentTarget;
    setPosition(getPosition(e, container));
    const onMove = ev => {
      ev.preventDefault();
      setPosition(getPosition(ev, container));
    };
    const onUp = () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
      window.removeEventListener("touchmove", onMove);
      window.removeEventListener("touchend", onUp);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    window.addEventListener("touchmove", onMove, {
      passive: false
    });
    window.addEventListener("touchend", onUp);
  };
  return <div className="not-prose" style={{
    borderRadius: "1rem",
    overflow: "hidden",
    height,
    width: "100%"
  }}>
      <div onMouseDown={onPointerDown} onTouchStart={onPointerDown} onClick={e => {
    e.preventDefault();
    e.stopPropagation();
  }} style={{
    position: "relative",
    width: "100%",
    height,
    overflow: "hidden",
    cursor: "ew-resize",
    userSelect: "none",
    WebkitUserSelect: "none"
  }}>
        {}
        <img src={afterImage} alt={afterLabel} draggable={false} style={{
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%",
    objectFit,
    pointerEvents: "none"
  }} />

        {}
        <div style={{
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%",
    clipPath: `inset(0 ${100 - position}% 0 0)`
  }}>
          <img src={beforeImage} alt={beforeLabel} draggable={false} style={{
    display: "block",
    width: "100%",
    height: "100%",
    objectFit,
    pointerEvents: "none"
  }} />
        </div>

        {}
        <div style={{
    position: "absolute",
    top: 0,
    left: `${position}%`,
    transform: "translateX(-50%)",
    width: "3px",
    height: "100%",
    background: "rgba(255,255,255,0.85)",
    pointerEvents: "none",
    zIndex: 2
  }} />

        {}
        <div style={{
    position: "absolute",
    top: "50%",
    left: `${position}%`,
    transform: "translate(-50%, -50%)",
    width: "44px",
    height: "44px",
    borderRadius: "50%",
    background: "rgba(255,255,255,0.95)",
    border: "2px solid rgba(0,0,0,0.15)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    gap: "6px",
    zIndex: 3,
    pointerEvents: "none",
    boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
  }}>
          {}
          <svg width="10" height="14" viewBox="0 0 10 14" fill="none" style={{
    marginRight: "-2px"
  }}>
            <path d="M8 1L2 7L8 13" stroke="#333" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
          {}
          <svg width="10" height="14" viewBox="0 0 10 14" fill="none" style={{
    marginLeft: "-2px"
  }}>
            <path d="M2 1L8 7L2 13" stroke="#333" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div>

        {}
        <div style={{
    position: "absolute",
    top: "12px",
    left: "12px",
    padding: "4px 10px",
    borderRadius: "6px",
    background: "rgba(0,0,0,0.55)",
    backdropFilter: "blur(4px)",
    color: "#fff",
    fontSize: "0.75rem",
    fontWeight: 600,
    letterSpacing: "0.02em",
    pointerEvents: "none",
    zIndex: 4
  }}>
          {beforeLabel}
        </div>
        <div style={{
    position: "absolute",
    top: "12px",
    right: "12px",
    padding: "4px 10px",
    borderRadius: "6px",
    background: "rgba(0,0,0,0.55)",
    backdropFilter: "blur(4px)",
    color: "#fff",
    fontSize: "0.75rem",
    fontWeight: 600,
    letterSpacing: "0.02em",
    pointerEvents: "none",
    zIndex: 4
  }}>
          {afterLabel}
        </div>

        {}
        {garmentImage && <div onMouseDown={e => e.stopPropagation()} onTouchStart={e => e.stopPropagation()} onClick={e => {
    e.preventDefault();
    e.stopPropagation();
    openLightbox();
  }} title={`${garmentLabel} — click to enlarge`} style={{
    position: "absolute",
    bottom: "12px",
    right: "12px",
    height: "140px",
    maxWidth: "40%",
    borderRadius: "8px",
    overflow: "hidden",
    cursor: "zoom-in",
    background: "rgba(255,255,255,0.95)",
    border: "2px solid rgba(255,255,255,0.9)",
    boxShadow: "0 2px 12px rgba(0,0,0,0.45)",
    zIndex: 5,
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  }}>
            <img src={garmentImage} alt={garmentLabel} draggable={false} style={{
    height: "100%",
    width: "auto",
    objectFit: "contain",
    pointerEvents: "none"
  }} />
          </div>}
      </div>

      {}
      {garmentImage && <dialog ref={dialogRef} onClick={closeLightbox} onClose={closeLightbox} style={{
    padding: 0,
    border: "none",
    background: "transparent",
    maxWidth: "100vw",
    maxHeight: "100vh",
    width: "100vw",
    height: "100vh",
    overflow: "hidden"
  }}>
          <div onClick={closeLightbox} style={{
    width: "100vw",
    height: "100vh",
    background: "rgba(0,0,0,0.88)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    cursor: "zoom-out",
    padding: "40px",
    boxSizing: "border-box"
  }}>
            <img src={garmentImage} alt={garmentLabel} onClick={e => e.stopPropagation()} style={{
    maxWidth: "90vw",
    maxHeight: "90vh",
    objectFit: "contain",
    borderRadius: "8px",
    boxShadow: "0 8px 32px rgba(0,0,0,0.6)",
    cursor: "default"
  }} />
          </div>
        </dialog>}
    </div>;
};

FLUX Outpainting extends an image naturally in any direction, filling new regions with contextually coherent content in a single call. Useful for aspect-ratio changes, banner generation, social media reformatting, or giving a composition more room to breathe.

<Note>
  Try it interactively and for free in our [FLUX Outpainting demo](https://flux-tools.bfl.ai/outpainting).
</Note>

## Example output

Drag the slider to compare the input image padded onto the target canvas (left) with the outpainted result (right). No prompt was used — the model extended the scene on its own.

<ImageComparisonSlider beforeImage="https://cdn.sanity.io/images/2gpum2i6/production/8d3f474924f13251796af6055f04ad7784a8865a-1200x624.png" afterImage="https://cdn.sanity.io/images/2gpum2i6/production/2e7bc4279ddd5e9dcb62d5f70444c892fa2b21dd-1200x624.png" beforeLabel="Input image on canvas" afterLabel="Outpainted result" height="380px" objectFit="contain" />

## Endpoint

Submit an outpainting job:

```http theme={null}
POST https://api.bfl.ai/v1/flux-tools/outpainting-v1
x-key: $BFL_API_KEY
```

Poll for the result:

```http theme={null}
GET https://api.bfl.ai/v1/get_result?id=<TASK_ID>
x-key: $BFL_API_KEY
```

## Quick start

The API uses an asynchronous workflow:

<Steps>
  <Step title="Submit an outpainting request">
    POST your input image (base64) and the target canvas dimensions to the endpoint. The model extends the existing scene naturally — no prompt is needed.
  </Step>

  <Step title="Poll for the result">
    Use the returned `polling_url` to check status until the image is ready.
  </Step>
</Steps>

<CodeGroup>
  ```python Python theme={null}
  #!/usr/bin/env python3
  import base64
  import os
  import time
  import requests

  API_KEY = os.environ["BFL_API_KEY"]
  BASE = "https://api.bfl.ai"
  HEADERS = {"accept": "application/json", "x-key": API_KEY, "Content-Type": "application/json"}

  IMAGE_PATH = "/path/to/input.png"
  WIDTH, HEIGHT = 1024, 1024
  REFERENCE_OFFSET_X = 100   # None = center horizontally
  REFERENCE_OFFSET_Y = 50    # None = center vertically

  with open(IMAGE_PATH, "rb") as f:
      image_b64 = base64.b64encode(f.read()).decode()

  payload = {
      "input_image": image_b64,
      "width": WIDTH,
      "height": HEIGHT,
      "output_format": "png",
  }

  if REFERENCE_OFFSET_X is not None:
      payload["reference_offset_x"] = REFERENCE_OFFSET_X
  if REFERENCE_OFFSET_Y is not None:
      payload["reference_offset_y"] = REFERENCE_OFFSET_Y

  submit = requests.post(f"{BASE}/v1/flux-tools/outpainting-v1", headers=HEADERS, json=payload)
  submit.raise_for_status()
  meta = submit.json()

  task_id = meta["id"]
  poll_url = meta.get("polling_url", f"{BASE}/v1/get_result?id={task_id}")

  while True:
      r = requests.get(poll_url, headers={"accept": "application/json", "x-key": API_KEY})
      r.raise_for_status()
      result = r.json()

      status = result.get("status")
      if status == "Ready":
          print("Result URL:", result["result"]["sample"])
          break
      if status in {"Error", "Request Moderated", "Content Moderated", "Task not found"}:
          raise RuntimeError(f"Outpainting failed with status: {status} | payload: {result}")

      time.sleep(1)
  ```

  ```bash cURL theme={null}
  IMAGE_B64="$(base64 < /path/to/input.png | tr -d '\n')"

  jq -n \
    --arg img "$IMAGE_B64" \
    '{
      input_image: $img,
      width: 1024,
      height: 1024,
      reference_offset_x: 100,
      reference_offset_y: 50,
      output_format: "png"
    }' > /tmp/outpaint_request.json

  curl -sS -X POST "https://api.bfl.ai/v1/flux-tools/outpainting-v1" \
    -H "accept: application/json" \
    -H "Content-Type: application/json" \
    -H "x-key: $BFL_API_KEY" \
    --data-binary @/tmp/outpaint_request.json

  # Poll for the result
  curl -sS "https://api.bfl.ai/v1/get_result?id=YOUR_TASK_ID" \
    -H "accept: application/json" \
    -H "x-key: $BFL_API_KEY"
  ```
</CodeGroup>

## Request parameters

| Parameter            | Type          | Required | Description                                                                                                                                            |
| -------------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `input_image`        | base64 string | Yes      | Reference image to expand                                                                                                                              |
| `width`              | integer       | Yes      | Target canvas width in pixels (`>=64`). `width × height` must not exceed 4 MP                                                                          |
| `height`             | integer       | Yes      | Target canvas height in pixels (`>=64`). `width × height` must not exceed 4 MP                                                                         |
| `reference_offset_x` | integer       | No       | Left offset (px) of the reference image's top-left corner on the canvas. Negative values allowed. `None` = center horizontally                         |
| `reference_offset_y` | integer       | No       | Top offset (px) of the reference image's top-left corner on the canvas. Negative values allowed. `None` = center vertically                            |
| `auto_crop`          | boolean       | No       | If `true`, crop the reference image to the canvas bounds when it extends beyond the edges. Defaults to `false` (out-of-bounds placements return `422`) |
| `mode`               | string        | No       | Quality/speed trade-off: `high` (default) or `fast`. See [Choosing a mode](#choosing-a-mode)                                                           |
| `output_format`      | string        | No       | `png` (default) or `jpeg`                                                                                                                              |

<Note>
  **Maximum canvas size: 4 MP.** The target canvas area (`width × height`) cannot exceed 4 megapixels (4,194,304 pixels). You're free to choose any aspect ratio within that budget — for example `2048 × 2048`, `2730 × 1536` (16:9), or `1024 × 4096` — as long as the two dimensions multiplied together stay at or below 4 MP. Requests above this limit return `422`.
</Note>

### Image placement

`reference_offset_x` and `reference_offset_y` set the top-left corner of the reference image on the output canvas. Drag the reference below to see how the offsets relate to the canvas:

<OutpaintingPlacementDemo />

You have two options for placing the reference image on the output canvas:

* **Centered (default)** — provide the image, set `width` and `height`, and leave `reference_offset_x` / `reference_offset_y` as `None`. The image is centered automatically.
* **Custom position** — set `reference_offset_x` and `reference_offset_y` to control exactly where the top-left corner of the reference image lands on the canvas. Negative values are allowed; if any part of the reference falls outside the canvas, either set `auto_crop: true` or the request will return `422`.

## Choosing a mode

The `mode` parameter trades quality for speed. Both modes accept the same request fields (`input_image`, `width`, `height`, `reference_offset_x` / `reference_offset_y`, `auto_crop`, `prompt`) and produce the same response format.

| Mode             | Speed                | Best for                                                                                                                                                                        |
| ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `high` (default) | Slower               | The highest-fidelity results — recommended whenever fine detail, prompt adherence, or consistency with complex content in the source image matters.                             |
| `fast`           | Significantly faster | Naturally extending most scenes — landscapes, backgrounds, textures, and products. May produce lower fidelity in the extended region than `high`. Typically lower cost as well. |

If you omit `mode`, the request runs in `high` mode.

```python theme={null}
payload = {
    "input_image": image_b64,
    "width": 1024,
    "height": 1024,
    "mode": "fast",          # opt into the faster path
    "output_format": "png",
}
```

<Note>
  **Fast mode requirements.** Fast mode encodes the reference and pads it in latent space, so a few extra constraints apply on top of the shared ones. Requests that violate them return `422`:

  * `input_image` must be a **base64-encoded image** — image URLs are not supported in `fast` mode.
  * The placed reference must be **at least 64 px per side** and have an **aspect ratio of at most 8:1**.
  * The canvas (`width × height`) plus internal alignment padding must stay **at or below 4 MP**. A canvas right at the 4 MP limit may be rejected because of the added padding — reduce it slightly if so.

  Any `reference_offset_x` / `reference_offset_y`, `width`, and `height` are otherwise accepted; you don't need to pre-align them.
</Note>

## Response format

### Initial response

```json theme={null}
{
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "polling_url": "https://api.bfl.ai/v1/get_result?id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

Always poll the URL returned in the response.

### Polling response (success)

```json theme={null}
{
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "Ready",
  "result": {
    "sample": "https://delivery.bfl.ai/..."
  }
}
```

When `status` is `"Ready"`, use `result.sample`.

<Warning>
  Signed delivery URLs are only valid for **10 minutes**. Retrieve your result within this timeframe.
</Warning>

## Tips for best results

* The model extends the existing scene naturally. The endpoint is tuned to continue the input image's content, lighting, and composition on its own.
* The model was trained on green, blue, and magenta fill colors and performs best with those internally — no caller action needed; the server handles fill colors automatically.
* Keep total output dimensions reasonable. The canvas area (`width × height`) is capped at 4 MP, and very large canvases or extreme aspect ratios may reduce quality even within that limit.

## Troubleshooting

* **`403 Forbidden`** — your API key is missing or your project doesn't have access to this endpoint.
* **`422` / validation errors** — check base64 encoding and that `width` / `height` are present, at least `64`, and that their product (`width × height`) does not exceed 4 MP. The endpoint rejects unknown fields: use `reference_offset_x` / `reference_offset_y` (not the older `bbox_x1` / `bbox_y1`).
* **`422` in `fast` mode specifically** — fast mode adds extra geometry constraints (see [Choosing a mode](#choosing-a-mode)): pass a base64 image (not a URL), keep the placed reference ≥ 64 px per side and within an 8:1 aspect ratio, and leave headroom below 4 MP so internal alignment padding still fits. Switch to `mode: "high"` if your placement can't satisfy these.
* **Visible seams** — give the model more canvas room around the reference image.

For the full list of HTTP status codes and polling response types returned by the API, see the [Errors reference](/api_integration/errors).
