Embedding YouTube transcripts into your own SaaS product â a content tool, a research platform, a video analytics dashboard â means treating Scriptube as a backend service your users never see directly. The integration pattern is the same whether you're serving ten users or ten thousand: resolve what's already cached instantly, queue what isn't, and tag every request so you can match results back to your own customers.
Why resolve, not submit, is the partner entry point
Most video requests from a mature product are repeats â the same popular videos get asked for again and again across different users. POST /api/v1/transcripts/resolve is built around that: cached videos come back inline in the same response, with no polling and no job to track. Only genuinely new videos get a batch_id to poll or wait on:
curl -X POST https://scriptube.me/api/v1/transcripts/resolve \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: a-client-generated-uuid" \
-d '{
"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
"external_id": "customer-4821-request-9",
"metadata": {"customer_id": "4821", "feature": "transcript-widget"}
}'
Every video is still charged the full processing price whether it's a fresh fetch or something Scriptube already held â that's the trade for getting an instant answer back instead of a job id (FACTS: "every processed video is charged, even if we already had it â that's why results can be instant"). Design your own pricing to that model rather than assuming a cache hit is free on your side too.
Send an Idempotency-Key header on every resolve call. If your own retry logic re-sends the same request after a timeout, a matching key replays the original result instead of billing the request twice.
Scaling past the inline cap
Resolve answers small requests inline; a larger request (above the inline cache cap, 50 by default) comes back with cached_inline: false and a list of cached_video_ids instead of full payloads. Page through those with POST /api/v1/transcripts/fetch, which is a free re-download of anything your account has already purchased:
curl -X POST https://scriptube.me/api/v1/transcripts/fetch \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_ids": ["dQw4w9WgXcQ", "..."]}'
fetch is the endpoint your product should call every time it needs to re-show a transcript it's already paid for â a user reopening a previous result, a cache in your own app expiring. It never charges credits; only first-time processing does.
Attribution: matching results back to your own customers
Three fields exist specifically for multi-tenant partners, and all three come back on every batch response and every webhook payload about that batch:
| Field | Limit | Use it for |
|---|---|---|
external_id | 128 chars, indexed | Your own id for the request â filterable with GET /transcripts?external_id= |
metadata | JSON object, â¤4KB | Anything else you need echoed back verbatim (customer id, internal feature flag, request source) |
callback_url | 2048 chars, SSRF-validated | Receive batch.completed and transcript.ready for this one batch, without registering an account-wide webhook |
Set all three you need at submit time, on both /resolve and POST /api/v1/transcripts. If you're running one Scriptube account behind many of your own customers, this is what lets a webhook delivery or a polled batch tell you which customer it belongs to without a side lookup table.
Callbacks for the async remainder
Anything resolve couldn't answer inline gets a batch_id and processes in the background. Rather than polling, set callback_url on the same request and Scriptube posts to it when the batch finishes â signed with your account's webhook secret if you have one registered, or with a one-time callback_secret returned in the create response if you don't. Verify it the same way as any other delivery: X-Scriptube-Signature: sha256=<hex HMAC-SHA256(secret, "{timestamp}.{raw body}")>, timestamp within 5 minutes. See webhooks-vs-polling-youtube-transcripts for when to use a per-batch callback versus a registered account-wide webhook â the short version is: per-batch for one-off partner requests tied to a specific customer action, registered for anything account-wide like low-credit alerts.
Browsing what you already hold
GET /api/v1/library lists every video your account has purchased, with a channel_id filter and pagination â useful for building your own "previously processed" view inside your product without re-querying Scriptube per video. Content itself still comes from /fetch; /library is metadata only.
Handling scale and errors like a partner, not a single user
- Rate limits are keyed on your API key â different products or environments using separate keys don't throttle each other, so split keys by environment (staging vs. production) rather than sharing one.
- Read every error's
codefield, not just the HTTP status: a 429 (rate_limited) needs a short backoff, while a 402 means insufficient credits and is worth surfacing to your customer. - Paginate large batches with
skip/limitonGET /transcripts/{batch_id}(capped at 50 items per page) rather than assuming a batch of hundreds returns in one response â see paginate-large-youtube-transcript-batches.
A realistic request shape
Putting it together, a partner backend's typical call during steady state is a resolve with attribution and an idempotency key, falling back to a callback for the rare cache miss:
POST /api/v1/transcripts/resolve
Idempotency-Key: <per-request uuid>
{
"urls": ["..."],
"external_id": "org-782-batch-55",
"metadata": {"org_id": "782"},
"callback_url": "https://yourapp.example.com/hooks/scriptube"
}
Most calls resolve instantly; the occasional miss shows up later at your callback URL instead of blocking your own API response to your customer.
For the full endpoint and error reference, see youtube-transcript-api-complete-guide, and for retry semantics when an item fails mid-batch, idempotency-key-safe-retries-transcript-api.
Request a Pro or higher plan API key: /ui/pricing.