If you're calling the Scriptube API on behalf of your own customers, you need a way to tell which batch belongs to whom. external_id and metadata do exactly that: tag a batch at submit time, and get the same tag back everywhere the batch shows up â in reads, in exports, and in webhook payloads.
Without this, your only option is maintaining your own lookup table mapping each batch_id Scriptube hands you back to whatever request caused it â workable, but it's one more table to keep in sync, and one more place a webhook arriving before your own write commits can cause a race. Letting Scriptube carry the tag for you removes that table entirely for the common case.
Setting them
Both are optional fields on submit, available on POST /api/v1/transcripts and POST /api/v1/transcripts/resolve:
This is also the piece that makes webhooks usable for a multi-tenant integration in the first place â without it, a webhook tells you a batch finished, but a tenant-serving backend still needs to know whose.
| Field | Limit | Use it for |
|---|---|---|
external_id | 128 characters, indexed | One id you can filter on later â a customer id, job id, or ticket number. |
metadata | JSON object, ⤠4 KB | Anything else worth carrying along â stored verbatim, returned exactly as sent. |
import requests
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
BASE = "https://scriptube.me"
resp = requests.post(
f"{BASE}/api/v1/transcripts",
headers=HEADERS,
json={
"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
"external_id": "customer_482",
"metadata": {"workspace_id": "ws_19", "requested_by": "import-job-7"},
},
)
resp.raise_for_status()
print(resp.json()["batch_id"])
Finding batches by tag
external_id is indexed specifically so you can filter the batch list by it â useful for "show me every batch we've ever run for this customer":
# Later: find every batch created for that customer
batches = requests.get(
f"{BASE}/api/v1/transcripts",
headers=HEADERS,
params={"external_id": "customer_482"},
).json()
for b in batches["batches"]:
print(b["batch_id"], b["status"], b["external_id"])
A mixed request â videos plus playlists plus channels in one call â can create several batches at once. Each one carries the same submission_id, so GET /api/v1/transcripts?submission_id=... finds every batch one request produced, while external_id finds every batch across many requests for the same tenant. Use whichever matches the question you're asking.
Showing up in webhooks
If you've registered a webhook or passed a callback_url, every event for that batch carries its external_id and metadata in the payload's data object â your receiver doesn't need to call back into the API to know which customer a notification is about:
{
"event": "batch.completed",
"timestamp": "2026-10-28T12:00:00Z",
"data": {
"batch_id": "b2b1a7b0-...",
"status": "completed",
"item_count": 1,
"completed_count": 1,
"failed_count": 0,
"external_id": "customer_482",
"metadata": {"workspace_id": "ws_19", "requested_by": "import-job-7"}
}
}
See building a webhook receiver for verifying the signature on that payload before trusting it.
Choosing what goes in each field
external_id is indexed and filterable, so keep it to one short, stable identifier â a customer id or job id, not a sentence. metadata is unindexed and just stored and echoed verbatim, so it's the right place for anything richer: which plan tier the request came from, who on your team triggered it, a correlation id for your own tracing, or flags your own code checks later ({"priority": "high"}). Keep both scoped to what you (the API caller) need â neither field is visible to the end user whose video it is, and neither affects how Scriptube processes the batch.
One thing worth flagging explicitly: both fields are returned verbatim, including inside webhook payloads that go to whatever endpoint you registered. Don't put anything in metadata you wouldn't want sitting in a webhook delivery log â it's for your own bookkeeping, not a secure secret store.
A submit with metadata over 4 KB, or an external_id longer than 128 characters, is rejected with a 422 before anything is created or billed â validate client-side if you're constructing these fields dynamically, to fail fast instead of discovering the limit mid-batch.
Why this matters for multi-tenant apps
If you're embedding Scriptube transcripts in your own SaaS, one API key typically serves many of your customers. Without a tag, a webhook or a batch-list call tells you a batch finished, but not whose. external_id is the cheapest way to close that gap without maintaining your own mapping table â though you'll usually still want your own job id stored for lookups that go the other direction (from your customer's request to the batch, before you know the batch_id).
Frequently asked
Does external_id need to be unique? No â many batches can share the same external_id, which is exactly the point: one customer or job id tagging every batch you've ever run for it.
Can I add or change the tags after a batch is already submitted? No, both fields are set at submit time and are read-only afterward â decide what to tag a batch with before you send the request.
Does this work on the plain submit endpoint too, or only /resolve? Both â external_id, metadata, and callback_url are accepted on POST /api/v1/transcripts and POST /api/v1/transcripts/resolve alike, and on the dashboard's own batch form.
What if I only have submission_id, not external_id, for a batch I'm looking at? GET /api/v1/transcripts/{batch_id} returns both external_id and submission_id on any batch read, plus related_batches for the siblings one mixed request created â you can always get from one to the other.
Next steps
- Webhooks vs. polling â where these tags surface.
- Embedding transcripts in your SaaS â the multi-tenant case this is built for.
- The complete API guide.
Full reference: API documentation.