Skip to content
reelspanDocs

Guides

Retrying safely

Use an Idempotency-Key header or your own request_id so a post retried after a timeout or network error is never published twice.

A network error can hide whether your post went out: the request may have reached Reelspan and the answer been lost on the way back. Retrying blindly could publish the same video twice. Send an Idempotency-Key header on every post, and a retry with the same key returns the first request's result instead of posting again.

curl -X POST https://api.reelspan.com/v1/posts/video \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -H "Idempotency-Key: launch-2026-12-01" \
  -F "user=my-brand" \
  -F "platform[]=tiktok" \
  -F "title=Launch day" \
  -F "[email protected]"

How the key works

The key is any string you choose, up to 200 characters (longer keys are cut to 200). X-Idempotency-Key is accepted as another name for the header, and X-Request-Id as a third (see Your own request_id); when more than one is sent, Idempotency-Key wins, then X-Idempotency-Key. It works on the four posting endpoints: POST /v1/posts/video, /photos, /text and /document.

You send the same key… What happens
Within 24 hours of the first request No new post. You get the first request's current state: its results if every network has finished, the background shape (message, request_id, total_platforms) if it is still running, or the 202 with job_id if it is scheduled. The response carries the header Idempotent-Replayed: true and "idempotent_replay": true in the body.
While the first request is still being received or checked 409 with error_code: IDEMPOTENCY_KEY_IN_USE. Wait a few seconds and send it again.
After 24 hours The key is free again, and the request is treated as a new post.
On a different kind of post Keys are kept separately for video, photo, text and document posts, so the same string on /posts/video and /posts/text makes two posts.

Keys belong to your workspace: every API key in it shares them, and no other workspace can see or collide with them.

Reelspan does not compare the body of a replayed request with the first one. A second request with the same key and a different caption, video or network list gets the first request's result, and nothing new is posted. Use a new key for every distinct post, for example one derived from your own record id.

A request that is refused outright (a 4xx such as a missing field or a file too large) creates no post, so its key is not used up: fix the request and send it again with the same key.

Your own request_id

You can also choose the post's request_id yourself: 8 to 100 letters, digits, _ or -, sent as a form field (or JSON field on text posts). Because you know it before sending, you can poll Post status with it even if the response never arrived.

curl -X POST https://api.reelspan.com/v1/posts/text \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": "my-brand", "platform": ["x", "bluesky"], "title": "We are live", "request_id": "order-58213-announce"}'

The X-Request-Id header does the same, for clients (such as ones written for Upload-Post) that send the id as a header. It must have the same shape, 8 to 100 letters, digits, _ or -; anything else (a proxy's own trace id, for example) is ignored. A request_id form field wins over the header. When the request has no Idempotency-Key, the header's value is also used as the idempotency key, so a repeat within 24 hours gets the first answer back rather than a 409.

curl -X POST https://api.reelspan.com/v1/posts/text   -H "Authorization: Bearer $REELSPAN_API_KEY"   -H "X-Request-Id: order-58213-announce"   -H "X-External-Id: order-58213"   -H "Content-Type: application/json"   -d '{"user": "my-brand", "platform": ["x", "bluesky"], "title": "We are live"}'

X-External-Id is the header form of the external_id field (your own reference, up to 255 characters, returned in history, status and webhooks); the field wins when both are sent.

A request_id can be used once, ever. Sending it again is refused with 409 REQUEST_ID_IN_USE and never posts twice; read the existing request's status instead. This is stricter than Idempotency-Key, which replays the result for you, so the two work well together: the key for automatic retries, your own request_id for looking a post up later.

Retrying in your own code

Retry on network errors, 408, 409 IDEMPOTENCY_KEY_IN_USE, 429 and 5xx, keeping the same key, with a growing pause. On a 429, wait for the Retry-After seconds. Don't retry other 4xx answers: they will fail the same way.

for attempt in 1 2 3 4 5; do
  code=$(curl -s -o out.json -w "%{http_code}" -X POST https://api.reelspan.com/v1/posts/video \
    -H "Authorization: Bearer $REELSPAN_API_KEY" \
    -H "Idempotency-Key: launch-2026-12-01" \
    -F "user=my-brand" -F "platform[]=tiktok" -F "title=Launch day" -F "[email protected]")
  case "$code" in 408|409|429|5*|000) sleep $((2 ** attempt)) ;; *) break ;; esac
done

The SDKs do it for you

The Node.js and Python SDKs give every post its own random key and reuse it across their automatic retries. They retry network errors, 408, 409, 429 and 5xx (twice by default, set with maxRetries / max_retries), honour Retry-After, and only retry a POST when it carries a key. You don't need to do anything.

The automatic key lives only for that one call. If your own code catches an error and calls posts.video again, that is a new call with a new key. To make retries safe across separate calls, or across a restart of your worker, pass your own idempotencyKey (Node) / idempotency_key (Python), as in the example above.

Networks that fail temporarily on their side are retried by Reelspan itself, and a network that failed for good can be sent again with Retry without uploading the media again. Neither needs a new post request.

Something unclear or missing? Write to [email protected] and we’ll fix the page.