# Edit Image

> Edit existing images with a natural-language instruction and one or more reference images.

Image editing applies a text instruction to one or more reference images while preserving the original structure. Like generation, it is synchronous by default and returns the edited image URL — and like generation, supplying a delivery target switches it to the asynchronous `202` contract. See [Async Image Generation](/docs/developer-tools/async-image-generation).

## `POST /v1/images/edit`

Edit an image using a text instruction and reference image URL(s).

**Auth:** API Key

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `instruction` | `string` | Yes | What to change, in natural language. Required. Maximum 4000 characters. |
| `image_urls` | `string[]` | Yes | URLs of images to edit. Required — between 1 and 5 images. |
| `model` | `string` | No | Model id. Defaults to gemini-3.1-flash-image-preview. |
| `size` | `string` | No | Output quality: 1K, 2K, or 4K. Optional; the API uses 1K when omitted. |
| `callback_url` | `string` | No | Public HTTPS URL to POST the result to. **Supplying this returns 202 instead of 200.** Maximum 2048 characters; private and loopback addresses are rejected. |
| `webhook` | `object` | No | Delivery target as `{"id": "…"}` (a webhook registered in the console) or `{"url": "…"}` (one-off), with an optional `events` array. Also returns 202. |
| `idempotency_key` | `string` | No | Body-level twin of the `Idempotency-Key` header. The header wins if both are sent. |

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | `string` | No | Deduplicate retried requests. If a request with the same key was already completed, the original response is returned without re-billing credits. 8–128 URL-safe characters. |

**Response — 200, synchronous (no delivery target)**

```json
{
  "id": "img_def456abc123",
  "url": "https://cdn.picxstudio.com/edited/def456.png",
  "model": "gemini-3.1-flash-image-preview",
  "size": "2K",
  "aspect_ratio": null,
  "credits_used": 35,
  "created_at": "2026-06-17T20:00:00Z"
}
```

**Response — 202, asynchronous (`callback_url` or `webhook` supplied)**

```json
{
  "id": "b68b2362-4bcc-48ff-b5cd-fbbd2ca34dd8",
  "status": "pending",
  "type": "image",
  "model": "gemini-3.1-flash-image-preview",
  "poll_url": "/v1/generations/b68b2362-4bcc-48ff-b5cd-fbbd2ca34dd8",
  "events_url": "/v1/generations/b68b2362-4bcc-48ff-b5cd-fbbd2ca34dd8/events",
  "webhook": {
    "mode": "inline",
    "id": null,
    "url": "https://your-server.com/hooks/picx",
    "events": ["generation.completed", "generation.failed"]
  }
}
```

Edits submitted this way arrive as a `generation.completed` event with `data.type` of `"image"`, exactly like a generation — the delivery payload does not distinguish edit from generate, so correlate on the `id` you were given.

## Edit an image

**JavaScript SDK**

```bash
npm install picx-ai
```

```js
import { PicX } from "picx-ai";

const picx = new PicX(process.env.PICX_API_KEY);

const edited = await picx.images.edit({
  instruction: "make the colors more vibrant and add a subtle glow effect",
  image_urls: ["https://cdn.picxstudio.com/api/generated/image_abc123.png"],
  model: "gemini-3.1-flash-image-preview",
  size: "1K",
});

console.log(edited.url);
console.log(edited.credits_used);
```

**Python SDK**

```bash
pip install picx-ai
```

```python
import os
from picx import PicX

picx = PicX(os.environ["PICX_API_KEY"])

edited = picx.images.edit(
    instruction="make the colors more vibrant and add a subtle glow effect",
    image_urls=["https://cdn.picxstudio.com/api/generated/image_abc123.png"],
    model="gemini-3.1-flash-image-preview",
    size="1K",
)

print(edited.url)
print(edited.credits_used)
```

**curl**

```bash
curl -X POST https://api.picxstudio.com/v1/images/edit \
  -H "Authorization: Bearer pxsk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"instruction":"make the colors more vibrant and add a subtle glow effect","image_urls":["https://cdn.picxstudio.com/api/generated/image_abc123.png"],"model":"gemini-3.1-flash-image-preview","size":"1K"}'
```

## Multiple reference images

Pass several URLs to composite or use additional references:

```js
const composite = await picx.images.edit({
  instruction: "place the product from the second image onto the background of the first",
  image_urls: [
    "https://example.com/background.jpg",
    "https://example.com/product.png",
  ],
  size: "2K",
});
```

```python
composite = picx.images.edit(
    instruction="place the product from the second image onto the background of the first",
    image_urls=[
        "https://example.com/background.jpg",
        "https://example.com/product.png",
    ],
    size="2K",
)
```

> [!TIP]
> `image_urls` accepts 1–5 URLs. More than five is rejected with a validation error before the request is sent, so you fail fast instead of burning a round trip.

> [!NOTE]
> Be explicit with verbs like add, remove, change, or replace. Clear, well-lit reference images produce the best edits.

## FAQ

### How many reference images can I edit with in one request?

Between 1 and 5. Passing more than five is rejected with a validation error before the request is sent, so you fail fast instead of burning a round trip.

### Can I composite two images together, not just edit one?

Yes — pass multiple `image_urls` and describe the composition in the `instruction`, e.g. placing a product from one image onto the background of another. See "Multiple reference images" above.

### Does editing preserve the original image's structure?

Yes — the instruction is applied while preserving the original structure, rather than regenerating the image from scratch.

### Is edit-image synchronous, like generate-image?

Yes. It waits for the edit and returns the edited image's hosted URL directly in the response — there's no job to poll.

### How do I avoid double-billing on a retried request?

Send an `Idempotency-Key` header, exactly as with [image generation](/docs/api-reference/generate-image) — a repeated request with the same key returns the original response without re-billing credits.
