Static Ads Lab
Concepts

Image ads

The core API output — Meta-ready PNGs generated from a brand, product, audience, and design template.

An image ad is the API output. You combine a design template with a brand, product, and audience, and the API generates a Meta-ready PNG asynchronously.

Mental model

Two SKUs:

  • Flat image ad (flat_image_ad, $1.00) — runs the Perfect Ad pipeline only. Output: a single 4K PNG. Use for high-volume, "generate-and-launch" workflows.
  • Editable image ad (editable_image_ad, $4.00) — runs the full pipeline (concept → Perfect Ad → image generation → template population → polish → final render). Output: a PNG plus a JSON tree you can mutate via PATCH and re-render. See Editable image ads.

ID prefix: ia_.

Fields

FieldTypeDescription
idstringia_…
design_template_id / brand_id / product_id / audience_idstringInputs
product_variant_idstring | nullOptional variant override
promptstring | nullFree-form creative direction
optionsobject | nullgenerate_ai_images, generate_copy toggles
editablebooleanIf true, ran the editable pipeline
sku_codeenumflat_image_ad or editable_image_ad
statusenumprocessing | completed | failed
progressobject | null{ step, message, percentage } while processing
image_urlstring | nullMeta-ready PNG (when complete)
width / heightnumber | nullPixel dimensions
current_json_treeobject | nullEditable JSON tree (only for editable ads)
figma_render_url / initial_figma_render_urlstring | nullFigma sync renders
errorobject | null{ code, message } when failed
duration_msnumber | nullWorker wall-clock time
batch_idstring | nullSet when generated as part of a batch
created_at / completed_atstringISO timestamps

Lifecycle

stateDiagram-v2
  [*] --> processing: POST /v1/image-ads (202)
  processing --> completed: image_url ready
  processing --> failed: error populated
  completed --> [*]
  failed --> [*]

You're billed only on completed. failed jobs do not deduct from your wallet.

Endpoints

MethodPathPurpose
POST/v1/image-adsGenerate an image ad
GET/v1/image-adsList image ads (supports ids=, status=, batch_id=)
GET/v1/image-ads/:idGet an image ad (supports Accept: text/event-stream for SSE)
PATCH/v1/image-ads/:idUpdate the JSON tree (editable only)
DELETE/v1/image-ads/:idDelete an image ad
POST/v1/image-ads/syncSync from Figma
GET/v1/image-ads/:id/versionsList versions (editable only)
GET/v1/image-ads/:id/versions/:versionIdGet a version

Common patterns

Generate a flat ad

const response = await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    design_template_id: "dt_...",
    brand_id: "brand_...",
    product_id: "prod_...",
    audience_id: "aud_...",
  }),
});
const { data } = await response.json();
// data.id, data.status === "processing"

Generate an editable ad

await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    design_template_id: "dt_...",
    brand_id: "brand_...",
    product_id: "prod_...",
    audience_id: "aud_...",
    editable: true,
  }),
});

Override specific text or images before generation

await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    design_template_id: "dt_...",
    brand_id: "brand_...",
    product_id: "prod_...",
    audience_id: "aud_...",
    node_overrides: {
      "node-id-headline": { characters: "Limited time: 25% off" },
      "node-id-hero": { imageFillUrl: "https://cdn.example.com/hero.jpg" },
    },
  }),
});

Batch poll

When generating many ads, poll them in one request rather than N:

const ids = ["ia_a", "ia_b", "ia_c"].join(",");
const response = await fetch(
  `https://api.staticadslab.com/v1/image-ads?ids=${ids}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const { data } = await response.json();
const stillProcessing = data.filter((d) => d.status === "processing");

See Batch generation and Async jobs.

Pitfalls

  • product_id and brand_id must be linked. Using a product that doesn't belong to the brand returns 422.
  • The design template must be in status: "completed" before you can use it.
  • Don't poll faster than every 2 seconds. 4 seconds is the recommended interval for image ads.
  • For editable ads, the current_json_tree is null until generation completes — wait for status: "completed" before reading it.

Prompt for your agent

Read https://www.staticadslab.com/docs/resources/image-ads.mdx and write generateAd({ designTemplateId, brandId, productId, audienceId, editable }) that creates an ad, polls every 4 seconds until completed, and returns the result. Throw on failed.