Every structured export format â json, jsonl, and csv â shares the same 22 columns, one row per video (or per stored caption track, if you export ?language=all). This is the field-by-field reference for writing a parser against GET /api/v1/transcripts/{batch_id}/export.
All three formats carry identical data â picking between them is purely about what's downstream: json for loading a whole batch into memory at once, jsonl for streaming processing or appending to a line-oriented data pipeline, csv for spreadsheets or anything expecting tabular input.
The columns
| Field | Meaning |
|---|---|
video_id, url, title, channel, channel_id | Video identity. |
duration_seconds, view_count, like_count, comment_count, published_at | Video stats, from include_metadata. |
description, tags | The video's YouTube description and tag list. |
transcript_language | Which caption language this row is. |
caption_source | manual (creator-uploaded) or asr (YouTube auto-generated). |
is_generated | Boolean version of the same thing. |
transcript_source, translation_source | How the text was obtained â e.g. a straight YouTube fetch vs. a Google translation. |
version | Archive version of the stored transcript (see GET /videos/{id}/transcripts?include_versions=true for older ones). |
text | The transcript itself. |
status, error_code, error | Set on failed/skipped items instead of a transcript â see below. |
Timecoded segments are not in these exports â they're flat, one row per video. For {text, start, duration} segments, use include_segments=true on the batch poll (GET /api/v1/transcripts/{batch_id}) instead, or export srt/vtt directly; see every way to get timestamps.
JSON â an array of rows
curl -s "https://scriptube.me/api/v1/transcripts/BATCH_ID/export?format=json" \
-H "X-API-Key: YOUR_API_KEY"
[
{
"video_id": "dQw4w9WgXcQ",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"title": "Rick Astley - Never Gonna Give You Up",
"channel": "Rick Astley",
"channel_id": "UCuAXFkgsw1L7xaCfnd5JJOw",
"duration_seconds": 213,
"view_count": 1600000000,
"like_count": 18000000,
"comment_count": 2100000,
"published_at": "2009-10-25",
"description": "The official video...",
"tags": ["rick astley", "never gonna give you up"],
"transcript_language": "en",
"caption_source": "manual",
"is_generated": false,
"transcript_source": "youtube_free",
"translation_source": null,
"version": 1,
"text": "We're no strangers to love...",
"status": "completed",
"error_code": "",
"error": ""
}
]
Note the top-level value is a JSON array, not one object per line â don't try to stream-parse it line by line.
JSONL â one object per line
curl -s "https://scriptube.me/api/v1/transcripts/BATCH_ID/export?format=jsonl" \
-H "X-API-Key: YOUR_API_KEY"
{"video_id": "dQw4w9WgXcQ", "url": "...", "title": "...", "status": "completed", "text": "We're no strangers to love...", ...}
{"video_id": "abc123XYZde", "url": "...", "title": "...", "status": "failed", "error_code": "transcripts_disabled", "error": "...", "text": ""}
This is the one to reach for when you're processing results with a streaming parser, or loading rows straight into a data pipeline â no need to buffer the whole array in memory first.
CSV â same columns, as a header row
curl -s "https://scriptube.me/api/v1/transcripts/BATCH_ID/export?format=csv" \
-H "X-API-Key: YOUR_API_KEY" -o export.csv
video_id,url,title,channel,channel_id,duration_seconds,view_count,like_count,comment_count,published_at,description,tags,transcript_language,caption_source,is_generated,transcript_source,translation_source,version,text,status,error_code,error
dQw4w9WgXcQ,https://www.youtube.com/watch?v=dQw4w9WgXcQ,Rick Astley - Never Gonna Give You Up,Rick Astley,UCuAXFkgsw1L7xaCfnd5JJOw,213,1600000000,18000000,2100000,2009-10-25,"The official video...","[""rick astley"", ""never gonna give you up""]",en,manual,False,youtube_free,,1,"We're no strangers to love...",completed,,
The tags column is a JSON array serialized as a single string (quoted and escaped), not a Python list â parse it as JSON, not as a comma-split field, or a video with multiple tags will look like extra columns. See opening CSV exports in Excel or Sheets for the spreadsheet side of this.
Per-item export
If you need one video's row rather than the whole batch, GET /api/v1/transcripts/{batch_id}/items/{item_id}/export returns the same schema for a single item â useful when a webhook's transcript.ready event hands you an item_id and you want just that row without pulling the whole batch.
Reading multiple languages per video
By default you get one row per video: the primary stored track (human captions before auto-generated, newest version). Pass ?language=all and a video with three stored caption tracks produces three rows instead of one, each with its own transcript_language and text â handy for a dataset that needs every language Scriptube has fetched for a video, not just the default. Without all, ?language=es picks one specific track and falls back to the default if that video doesn't have Spanish stored.
Failed and skipped items
By default (include_failed=true), a video that errored out still gets a row â status set to failed or skipped, error_code set to a machine-readable reason (e.g. transcripts_disabled, video_unavailable), text empty. Pass include_failed=false to drop them and get completed-only rows. Exporting a batch that's still running needs partial=true explicitly, or the export is refused until it finishes.
This matters for parsers more than it might seem: code that assumes every row has a non-empty text field will crash or silently produce garbage the first time it hits a real batch with any failures in it. Branch on status before touching text, every time.
Encoding and escaping, per format
| Format | How the transcript text is escaped |
|---|---|
| JSON / JSONL | Standard JSON string escaping â quotes, newlines, and unicode are handled by any JSON parser automatically. |
| CSV | RFC 4180 quoting â a field containing a comma, quote, or newline is wrapped in double quotes, with internal quotes doubled. Use a real CSV parser (not split(",")), since transcript text routinely contains commas. |
A transcript longer than a typical spreadsheet cell limit (Excel caps a cell around 32,767 characters) can be truncated when opened in Excel specifically â this is an Excel limitation on the receiving end, not something the export does. Long-form content is usually fine in Google Sheets or when read programmatically with pandas or a CSV library.
Next steps
- The complete API guide â submit, poll, export, errors.
- CSV in Excel/Sheets â filtering and pivoting these columns.
- SRT vs. VTT â for timecoded subtitle formats instead of flat rows.
Full reference: API documentation.