When to use it
Send an Idempotency-Key header on every POST mutation that costs credits or touches a third party:
- Video generation (
/api/v1/videos,/api/v1/generate-and-post, etc.) - Multi-platform posting
- Webhook subscription creation
- Anything that returns a
job_idor charges credits
GET and HEAD requests are naturally safe — the header is ignored.
Header shape
Idempotency-Key: 5f9e1a2b-4c8d-4e3f-9a1b-2c3d4e5f6a7b- Length: 8–256 characters.
- Format: Any printable ASCII. UUIDv4 is recommended (
crypto.randomUUID()in JS,uuid.uuid4()in Python). - Scope: Per API key. Two keys can re-use the same value without colliding.
- Lifetime: 24 hours from first observation. Storage is MongoDB with a TTL index — after the TTL, the key is forgotten and a new request with the same value executes fresh.
Replay behavior
When ReelsBuilder receives a POST with an Idempotency-Key:
- First time we see the key: The handler runs normally. The status code, headers, and response body are captured and stored.
- Second+ time (same body): The cached response is replayed byte-for-byte, including the original status code. No handler runs, no credits are charged, no external APIs are called. An
X-Idempotent-Replay: trueheader is added so you can detect replays in observability tooling. - Second+ time (different body): Returns
409 RESOURCE_ALREADY_EXISTSwith details about which fields differ. Generate a new key per logical operation — do not re-use a key for "a similar but slightly different" request. - After 24h: The key is reclaimed by the TTL index. A fresh request with the same value behaves like the first observation.
Examples
cURL
curl -X POST https://api.reelsbuilder.ai/api/v1/generate-and-post \
-H "Authorization: Bearer $REELSBUILDER_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"topic": "Q3 launch announcement",
"platforms": ["tiktok", "instagram", "youtube"]
}'TypeScript
const idempotencyKey = crypto.randomUUID();
async function callWithRetry() {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const res = await fetch("https://api.reelsbuilder.ai/api/v1/generate-and-post", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.REELSBUILDER_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({ topic: "Q3 launch", platforms: ["tiktok"] }),
});
if (res.ok) return await res.json();
if (res.status >= 500) throw new Error("retryable");
return await res.json();
} catch (err) {
if (attempt === 2) throw err;
await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
}
}
}Python
import os, uuid, time, requests
idempotency_key = str(uuid.uuid4())
def call_with_retry():
for attempt in range(3):
try:
r = requests.post(
"https://api.reelsbuilder.ai/api/v1/generate-and-post",
headers={
"Authorization": f"Bearer {os.environ['REELSBUILDER_API_KEY']}",
"Idempotency-Key": idempotency_key,
},
json={"topic": "Q3 launch", "platforms": ["tiktok"]},
timeout=30,
)
if r.ok:
return r.json()
if r.status_code >= 500:
raise RuntimeError("retryable")
return r.json()
except Exception:
if attempt == 2:
raise
time.sleep((2 ** attempt) * 0.5)Detecting replays
Every replayed response includes:
X-Idempotent-Replay: trueUse this for observability (count replays as a retry-health signal) but never as a correctness check — the response body itself is the truth.
Best practices
- One key per logical operation. "Schedule a daily news post for 2026-05-16" gets one key. "Generate a hook for topic X" gets another.
- Persist the key alongside the operation in your own store. On any retry — even after a process restart — you must re-send the same key.
- Use random UUIDv4. Do not derive the key from request content; a content hash would replay correctly on retries but block legitimate re-runs of the same operation.
- Don't recycle keys. After 24h the slot is freed but the operation may have a downstream side effect (a published video, a scheduled cron). Don't intentionally re-send the same key after the TTL just to trigger a re-run.