1. Get an API key
Create a free account, then copy your key from the dashboard. The free key includes 10 successful requests so you can test your integration. Pro includes 1,500 API caption requests per month, with bursts of up to 50 per minute.
Send the key on every request, either as Authorization: Bearer YOUR_API_KEY or as x-api-key: YOUR_API_KEY. Keep it on your server: don't put it in front-end code or commit it to a repository. Load it from an environment variable, as the examples below do.
2. Make your first request with cURL
The endpoint is /api/v1/transcript. Pass the video as url. A full watch URL, a youtu.be link, a Shorts link, or a bare 11-character video ID all work.
curl "https://www.tubetotranscript.com/api/v1/transcript?url=https://www.youtube.com/watch?v=VIDEO_ID" \
-H "Authorization: Bearer YOUR_API_KEY"You can also POST a JSON body. Add language (for example es) to request a specific caption track. Without it, the default track is returned.
curl -X POST "https://www.tubetotranscript.com/api/v1/transcript" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://youtu.be/VIDEO_ID", "language": "es"}'3. Understand the JSON response
{
"language": "en",
"language_name": "English",
"is_generated": false,
"length_seconds": 642,
"lengthText": "10:42",
"metadata": {
"videoId": "VIDEO_ID",
"title": "Video title",
"channel": "Channel name",
"thumbnail": "https://i.ytimg.com/vi/VIDEO_ID/hqdefault.jpg"
},
"transcript": [
{ "text": "Welcome back to the channel.", "start": 0.0, "duration": 2.4 },
{ "text": "Today we're looking at tides.", "start": 2.4, "duration": 3.1 }
],
"available_languages": [
{ "code": "en", "name": "English", "is_generated": false },
{ "code": "es", "name": "Spanish", "is_generated": true }
]
}- transcript is an array of caption segments in order.
startanddurationare numbers in seconds, rounded to two decimals, so a segment ends atstart + duration. - is_generated tells you whether the track came from YouTube's speech recognition. Treat those tracks as less reliable for names and numbers.
- available_languages lists every caption track the video has. Use one of those codes as
languagein a follow-up request. - metadata gives you the title, channel, and thumbnail, so you don't need a separate YouTube Data API call to label results.
4. Python example
Uses the requests library (pip install requests).
import os
import requests
API_KEY = os.environ["TUBETOTRANSCRIPT_API_KEY"]
resp = requests.get(
"https://www.tubetotranscript.com/api/v1/transcript",
params={"url": "https://www.youtube.com/watch?v=VIDEO_ID"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=60,
)
resp.raise_for_status()
data = resp.json()
print(data["metadata"]["title"])
for seg in data["transcript"]:
print(f'{seg["start"]:>7.2f}s {seg["text"]}')5. Node.js example
Uses the built-in fetch in Node 18 and later, with no extra packages.
const res = await fetch(
"https://www.tubetotranscript.com/api/v1/transcript?url=" +
encodeURIComponent("https://www.youtube.com/watch?v=VIDEO_ID"),
{ headers: { Authorization: `Bearer ${process.env.TUBETOTRANSCRIPT_API_KEY}` } }
);
if (!res.ok) {
const err = await res.json();
throw new Error(`${res.status} ${err.code}: ${err.error}`);
}
const data = await res.json();
const plainText = data.transcript.map((s) => s.text).join(" ");
console.log(plainText);6. Export to CSV or Excel
Because every segment has the same three fields, the transcript array maps directly onto a table. With pandas, you can go from API response to spreadsheet in three lines:
import pandas as pd
df = pd.DataFrame(data["transcript"]) # columns: text, start, duration
df["end"] = df["start"] + df["duration"]
df.to_csv("transcript.csv", index=False) # opens in Excel / Google Sheets
df.to_excel("transcript.xlsx", index=False) # needs: pip install openpyxlFor a one-off video, you don't need code: the YouTube to CSV tool downloads the same rows as a CSV file, and the YouTube to JSON tool downloads a JSON file from the browser.
7. Markdown for LLMs
If you are feeding the transcript straight into a language model, ask for Markdown instead of JSON by sending Accept: text/markdown. You get a title, video details, and one [MM:SS]-prefixed line per segment. The response includes an x-markdown-tokens header with a rough token estimate.
curl "https://www.tubetotranscript.com/api/v1/transcript?url=https://youtu.be/VIDEO_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: text/markdown"8. Handle errors
Errors return JSON with a human-readable error message and a machine-readable code:
| Status | Code | What to do |
|---|---|---|
| 401 | API_KEY_REQUIRED / INVALID_API_KEY | Check that the header is sent and the key is active. |
| 400 | INVALID_URL | The value isn't a recognizable YouTube link or video ID. |
| 4xx | TRANSCRIPT_DISABLED / TRANSCRIPT_NOT_AVAILABLE | The video has no usable captions. Don't retry. |
| 4xx | LANGUAGE_NOT_AVAILABLE | Pick a code from available_languages. |
| 429 | Rate limit or quota | Wait and retry with backoff, or upgrade if your quota is used up. |
Only retry on 429 and 5xx responses. A missing-captions error won't change on retry, and failed caption lookups don't count against your allowance.
More endpoints
The same key works for batch requests (many videos in one call), playlists, and channel search, and there is an MCP server for AI assistants such as Claude and Cursor. See the API documentation for every endpoint, or the API overview for plans and use cases.