Idempotency
Use Idempotency-Key on POST and PATCH to prevent duplicate work on retries.
The Static Ads Lab API supports idempotency for POST and PATCH requests via the Idempotency-Key header. Use it whenever you'd otherwise risk double-charging your wallet on retries.
How it works
- Send
Idempotency-Key: <unique-string>on aPOSTorPATCH. - The first request with that key (per API key) executes normally.
- Subsequent requests with the same key replay the cached response — no new work, no duplicate wallet charge.
- Cached responses include the header
Idempotent-Replayed: trueso you can tell the difference. - Keys are scoped per API key. Different keys with the same
Idempotency-Keyvalue don't collide. - Cache TTL is 24 hours by default (
IDEMPOTENCY_TTL_SECONDS). After that, the key is forgotten and a new request will execute.
Recommended key format
A UUID v4 is the simplest choice:
import { randomUUID } from "node:crypto";
const idempotencyKey = randomUUID();
await fetch("https://api.staticadslab.com/v1/image-ads", {
method: "POST",
headers: {
"X-API-Key": process.env.SAL_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
design_template_id: "dt_...",
brand_id: "brand_...",
product_id: "prod_...",
audience_id: "aud_...",
}),
});For deterministic flows, derive the key from a stable business identifier:
const idempotencyKey = `generate-ad:${userId}:${campaignId}:${variantIndex}`;What you should idempotize
POST /v1/image-ads— every flat ad costs $1.00, every editable ad costs $4.00. Always idempotize.POST /v1/design-templates— costs $0.25 each. Idempotize.PATCH /v1/image-ads/:id— re-runs the render. Idempotize if you might retry.
You don't need to idempotize read operations or operations on resources that have no side effects (e.g. updating a brand name).
Concurrent requests with the same key
If two requests with the same Idempotency-Key land at the same time:
- One acquires a 30-second lock and executes.
- The other returns
409 IDEMPOTENCY_CONFLICTwith a message asking you to retry shortly.
Add a brief retry-on-409 to your client.
Streaming endpoints
SSE responses (Accept: text/event-stream) are not cached — there's nothing to replay. The lock is released on completion, but the response body is not stored.
Pitfalls
- Reusing a key for a different request body. The cached response is returned regardless of body. Use a fresh key when the request changes.
- Using a non-unique key per logical operation. A literal
"my-key"reused across all requests will replay the first response forever (until TTL). - Treating idempotency as a deduplication mechanism for read endpoints — the API only honors it on
POSTandPATCH.