Blog / Tutorials / Idempotency-Key: Safe Retries on the Transcript API
Tutorials 5 min read â€ĸ November 13, 2026

Idempotency-Key: Safe Retries on the Transcript API

Founder

If a request to POST /api/v1/transcripts/resolve times out, you don't know whether it was billed before the connection dropped. Retrying blind risks charging the same batch twice. The Idempotency-Key header fixes that: send the same key on a retry, and Scriptube returns the original response instead of processing it again.

This matters specifically for /resolve because it can bill synchronously, inline, in the same request that times out. A dropped connection doesn't tell you whether the server finished processing before or after the network failed — from your side, a timeout and a successful-but-slow response look identical until you check.

How it works

Generate a key (a UUID is fine) once per logical request, and send it on every attempt of that request — not a new one per retry:

curl -s -X POST https://scriptube.me/api/v1/transcripts/resolve \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: 7f3a9e2c-1b4d-4e8a-9c7f-2d5e6a8b1c3d" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]}'

What happens on each attempt:

SituationResult
First time this key is usedProcessed and billed normally.
Same key, first call already finishedThe stored response is replayed — no new charge, no new batch.
Same key, first call still in flight409 — a concurrent call with that key is already running.
No key sentEvery call is processed and billed independently, as normal.

A replayed key answers from the stored result for 24 hours. The in-progress lock self-clears after 60 seconds, so a call that genuinely died doesn't leave the key stuck forever. Keys are scoped to your account, so reusing a short key is safe across different accounts.

Worth being deliberate about key choice: generate one key per logical submission and reuse it only for retries of that exact same request. Reusing a key across two genuinely different requests (different URLs, different options) isn't an error on Scriptube's side — you'll just silently get the first request's result back for the second one, which is a confusing bug to track down later. A random UUID per submission, kept only as long as you might still retry, is the simplest correct approach.

Using it in a retry loop

import uuid
import requests

HEADERS = {"X-API-Key": "YOUR_API_KEY"}
BASE = "https://scriptube.me"

idempotency_key = str(uuid.uuid4())  # generate once, reuse on retry

def submit():
    return requests.post(
        f"{BASE}/api/v1/transcripts/resolve",
        headers={**HEADERS, "Idempotency-Key": idempotency_key},
        json={"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]},
        timeout=10,
    )

try:
    r = submit()
except requests.exceptions.Timeout:
    # Network timeout: we don't know if the first request billed. Retry with
    # the SAME key instead of generating a new one.
    r = submit()

r.raise_for_status()
print(r.json())

The bug this prevents is subtle: a naive retry-on-timeout generates no key at all, or a fresh key each time, and a batch that actually succeeded server-side gets submitted — and billed — again.

Seeing the replay in practice

Two curl calls with the same key, one right after the other, demonstrate the behavior directly:

# First call — processed and billed normally
curl -s -X POST https://scriptube.me/api/v1/transcripts/resolve \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: demo-key-001" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]}'

# Second call, same key — returns the exact same response, no new charge
curl -s -X POST https://scriptube.me/api/v1/transcripts/resolve \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Idempotency-Key: demo-key-001" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]}'

If you fire both calls at nearly the same instant (genuinely concurrent, not one after the other), you'll instead see the second one answer 409 — the first hasn't finished yet, so there's no stored result to replay, and the key is locked to prevent a double-charge race.

What it doesn't cover

Idempotency-Key is specific to /resolve. It doesn't apply to POST /api/v1/transcripts (plain submit), exports, or lifecycle actions like retry-failed — those don't have the same "might have already billed" ambiguity in the same way, since a plain submit always returns a batch_id you can poll to check what actually happened. If you're already polling or using webhooks to confirm batch state, you may not need idempotency keys at all for those paths — see webhooks vs. polling.

Frequently asked

Do I need a new key for every retry attempt? No — the opposite. Generate one key for the logical request and send that same key on every attempt of it, however many times you retry.

What happens if I never retry — does the key do anything? Nothing extra. A key sent once, with no retry, behaves exactly like not sending one: the request is processed and billed normally, and the result happens to be cached under that key for 24 hours in case you do need to retry later.

Can two different API keys on the same account collide on the same idempotency key? No — the lock is scoped per account, not per API key, so any key belonging to the same account sees the same replay behavior for a given Idempotency-Key value.

Is there a format requirement for the key itself? No fixed format is enforced — a UUID is the conventional choice because it's trivially unique, but any sufficiently unique string works.

Does the replayed response reflect the current state of the batch, or a frozen snapshot? It's the original /resolve response exactly as it was returned the first time — for a batch that's still processing in the background, poll GET /api/v1/transcripts/{batch_id} for current status rather than relying on a replay to reflect progress.

Next steps

Full reference: API documentation.

Try Scriptube Free

Extract YouTube transcripts instantly. No credit card required.

Get Started

Related Articles

Tutorials

Your YouTube Transcript Library: Find Everything You've Already Processed

Every video you've ever submitted to Scriptube stays in your library — browsable by channel and playlist, searchable when you need to find something you...

Tutorials

How to Retry Failed YouTube Transcripts Without Resubmitting the Batch

A batch of 50 videos rarely comes back 50 for 50 — a couple of videos might have no captions, one might hit a rate limit, another might just need a second...

Tutorials

YouTube Transcript API: The Complete Guide

The Scriptube API lets you submit YouTube video, playlist, or channel URLs and get back transcripts and metadata as JSON, CSV, or JSONL, plus timed segments...