Get YouTube transcripts with Python. One call per video.
Timestamped lines and the full text of a YouTube video’s captions, saved as plain text or SRT subtitles. One API key. No YouTube login, OAuth or proxies.
MONOCRAWL_API_KEY=mn_••••••••Free plan · 1,000 credits a month · No card
What comes back
One call. The whole transcript.
A real response for Marques Brownlee’s iPhone 18 Pro review: 469 timed lines and 3,343 words of text, for 2 credits. The first spoken lines are below.
video_idThe YouTube video id
languageThe caption language
has_transcriptFalse when the video has no captions
transcriptThe full text as one string
segmentsEach line with start_ms and end_ms
So, if this were back in 2011, [music]
0:16–0:22
this is what we would call an S update.
0:19–0:24
The iPhone 18 Pro is basically an iPhone
0:22–0:26
17 Pro with a chip update, a camera
0:24–0:28
upgrade, and better battery life, plus
0:26–0:30
some new colors to make sure people know
0:28–0:32
it's the new one. That's an S update if
0:30–0:33
we've ever seen one. And I think it's
0:32–0:35
Show the JSON
{
"success": true,
"data": {
"platform": "youtube",
"video_id": "ohqxP8EEumo",
"url": "https://www.youtube.com/watch?v=ohqxP8EEumo",
"language": "English",
"has_transcript": true,
"transcript": "[music] [music] So, if this were back in 2011, [music] this is what we would call an S update. The iPhone 18 Pro is basically an iPhone 17 Pro with a chip update, a camera upgrade, and better battery life, plus some new colors to make sure people know it's the new one. That's an S update if we've ever seen one. And I think it's tempting to be disappointed in that, but after using it for the past week, I actually think it's underrated. I know, I know. Cue all the shill comments. What …",
"segments": [
{
"text": "[music]",
"start_ms": 1309,
"end_ms": 3329
},
{
"text": "[music]",
"start_ms": 7860,
"end_ms": 9880
},
{
"text": "So, if this were back in 2011, [music]",
"start_ms": 16560,
"end_ms": 22160
},
{
"text": "this is what we would call an S update.",
"start_ms": 19439,
"end_ms": 24480
},
{
"text": "The iPhone 18 Pro is basically an iPhone",
"start_ms": 22160,
"end_ms": 26880
},
{
"text": "17 Pro with a chip update, a camera",
"start_ms": 24480,
"end_ms": 28960
},
{
"text": "upgrade, and better battery life, plus",
"start_ms": 26880,
"end_ms": 30320
},
{
"text": "some new colors to make sure people know",
"start_ms": 28960,
"end_ms": 32399
},
{
"text": "it's the new one. That's an S update if",
"start_ms": 30320,
"end_ms": 33520
},
{
"text": "we've ever seen one. And I think it's",
"start_ms": 32399,
"end_ms": 35200
},
{
"text": "tempting to be disappointed in that, but",
"start_ms": 33520,
"end_ms": 37120
},
{
"text": "after using it for the past week, I",
"start_ms": 35200,
"end_ms": 38800
},
{
"text": "actually think it's underrated. I know,",
"start_ms": 37120,
"end_ms": 41040
},
{
"text": "I know. Cue all the shill comments. What",
"start_ms": 38800,
"end_ms": 42879
}
]
}
}
Cost
2 credits a video. Nothing for failures.
Each transcript costs 2 credits, whether the video runs one minute or an hour. The recorded 16:33 review cost 2. Slide to see what a batch costs.
200credits, 2 per video
500videos a month on the free plan
5,000on Starter, $29 a month
The batch endpoint charges the same per video, and leaves failed or empty items uncharged.
Two more things
Many videos, errors and rate limits.
Up to 20 videos in one call
POST a list of URLs or ids to youtube/transcripts. Each result has its own success flag, data and credits_used; failed or empty items are not charged.
batch.py
BATCH_URL = "https://www.monocrawl.com/v1/youtube/transcripts"
VIDEOS = ["https://www.youtube.com/watch?v=ohqxP8EEumo",
"https://www.youtube.com/watch?v=jNQXAC9IVRw"]
body = requests.post(BATCH_URL, json={"urls": VIDEOS}, headers=HEADERS, timeout=120).json()
for item in body["results"]:
if item["success"]:
print(item["input"], len(item["data"]["segments"]), "segments")
else:
print(item["input"], item["error"]["message"])
Errors and rate limits
A failed call returns success: false with an error type and message, and costs nothing. On HTTP 429, wait and try again.