Edit image ads with AI
Describe a change in plain language and get a corrected version of the same ad back — versioned, restorable, and billed only on success.
When a generated ad is almost right — wrong headline, a logo you want gone, a small element to swap — you don't need to regenerate it. POST /v1/image-ads/{id}/edit applies your plain-language instruction to the ad's current image and delivers the result as a new version of the same ad.
When to use this
- Changing headline or CTA copy on a finished ad.
- Removing or swapping a single element (logo, badge, background detail).
- Fixing small visual issues without losing the composition that already works.
AI edits work on flat image ads (editable: false). For editable ads, mutate the JSON tree instead — see Edit a JSON tree and re-render.
How it works
Each edit runs a maintained two-step pipeline: your instruction is first refined into a precise image-editing prompt (element targeting, preservation constraints), then applied to the ad's current image at its original aspect ratio and resolution. Behind the scenes this is versioned like a git history:
- The edit immediately creates a pending version (
status: "processing"). - On success the version completes and the ad's
image_urlmoves to the edited image. - On failure the version is marked
failed, the ad is untouched, and you are not charged. - Every previous image stays in the version history and can be restored for free.
$0.50 per successful edit (SKU image_ad_edit).
End-to-end flow
sequenceDiagram
participant App as Your app
participant API as Static Ads Lab API
App->>API: POST /v1/image-ads/:id/edit { prompt }
API-->>App: 202 { version, status: processing }
Note right of API: refine → apply pipeline
App->>API: GET /v1/image-ads/:id/versions/:versionId (poll)
API-->>App: completed { image_url }
Note right of API: ad's image_url now points at the editSubmit an edit
const response = await fetch(
`https://api.staticadslab.com/v1/image-ads/${imageAdId}/edit`,
{
method: "POST",
headers: {
"X-API-Key": process.env.STATICADS_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: "Remove the logo and change the headline to 'Summer Sale'",
}),
},
);
const { data: version } = await response.json();
// version.id → "iav_..." (poll this)
// version.status → "processing"
// version.version_number → 2Compound instructions are supported — put every change in one prompt, as in the example above. One edit runs at a time per ad: if a second edit is submitted while one is processing, the API returns 409 EDIT_ALREADY_IN_PROGRESS. A processing version older than 10 minutes is treated as abandoned and marked failed automatically (never charged), so a stuck run never blocks the ad's edits or restores.
Poll the version
async function waitForEdit(imageAdId, versionId, apiKey) {
while (true) {
const r = await fetch(
`https://api.staticadslab.com/v1/image-ads/${imageAdId}/versions/${versionId}`,
{ headers: { "X-API-Key": apiKey } },
);
const { data } = await r.json();
if (data.status === "completed") return data;
if (data.status === "failed") throw new Error("Edit failed — no charge was made");
await new Promise((res) => setTimeout(res, 3000));
}
}
const edited = await waitForEdit(imageAdId, version.id, apiKey);
console.log(edited.image_url); // the edited image (also the ad's new image_url)Version history
GET /v1/image-ads/{id}/versions lists every version. The first edit lazily creates v1 from the original image, so history always starts at the original:
const r = await fetch(
`https://api.staticadslab.com/v1/image-ads/${imageAdId}/versions`,
{ headers: { "X-API-Key": apiKey } },
);
const { data: versions } = await r.json();
// [
// { version_number: 1, source: "generation", status: "completed", ... },
// { version_number: 2, source: "ai_edit", edit_prompt: "Remove the logo...", ... },
// ]Key fields on each version:
| Field | Meaning |
|---|---|
status | processing, completed, or failed |
source | generation (the original), ai_edit, restore, or tree-snapshot sources for editable ads |
edit_prompt | Your instruction, on ai_edit versions |
image_url | The version's image (null while processing or failed) |
Restore a previous version
Restores are free and non-destructive: a new version is appended with the old image, and the ad's image_url moves to it. Nothing is ever deleted — a restore is itself restorable.
const r = await fetch(
`https://api.staticadslab.com/v1/image-ads/${imageAdId}/versions/${versionId}/restore`,
{
method: "POST",
headers: { "X-API-Key": apiKey },
},
);
const { data: restored } = await r.json();
// restored.source → "restore", restored.status → "completed"Errors
| Status | Code | Meaning |
|---|---|---|
402 | INSUFFICIENT_BALANCE | Wallet can't cover the $0.50 edit |
404 | IMAGE_AD_NOT_FOUND | Unknown ad id |
409 | EDIT_ALREADY_IN_PROGRESS | Another edit is processing on this ad — poll it, then retry |
422 | IMAGE_AD_EDIT_NOT_SUPPORTED | The ad is editable-mode; AI edits are flat-only |
422 | IMAGE_AD_NOT_COMPLETED | The ad hasn't finished generating |
422 | IMAGE_AD_VERSION_NOT_RESTORABLE | Restore target is processing or failed |