ScrapeMole
Home / Guides / YouTube transcripts from Python, curl and n8n

YouTube transcripts from Python, curl and n8n (one HTTP call, works from a server)

Get YouTube transcripts from a server in one HTTP call: curl, Python (apify-client) and n8n examples with real output, captionless-video handling, translation and cost per transcript.

Open the YouTube Transcript Scraper →By ScrapeMole · 23 October 2026

Disclosure: I'm the maker of the YouTube Transcript Scraper on the Apify Store used below. Other transcript tools exist; this post shows how to use ours, with real output.

If you've tried pulling YouTube transcripts in a script lately, you've probably hit one of these:

  • the "official" route needs OAuth and only works for videos you own,
  • the popular open-source libraries work on your laptop and then get blocked on a server,
  • or, the sneakiest one, the call "succeeds" and returns an empty transcript.

That last one happens because the caption links YouTube serves to the desktop site now need a hidden proof-of-origin token and quietly return nothing without it. This post shows a way that works from a server, with one HTTP call, from Python, from curl, or from n8n.

The one-call version (curl)

Apify Actors can be called synchronously: you POST the input and get the results back in the same response.

curl -X POST \
  "https://api.apify.com/v2/acts/scrapemole~youtube-transcript-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"videos": ["https://youtu.be/jNQXAC9IVRw"], "languages": ["en"]}'

You get a free token by signing up at Apify (the free plan includes monthly credit). The response is a JSON array with one row per video. Trimmed, from a real run on 23 October 2026:

[{
  "videoId": "jNQXAC9IVRw",
  "status": "ok",
  "title": "Me at the zoo",
  "channelName": "jawed",
  "language": "en",
  "isAutoGenerated": false,
  "transcriptText": "All right, so here we are, in front of the elephants ...",
  "segments": [{ "start": 1.2, "duration": 2.16, "end": 3.36, "text": "All right, so here we are, in front of the elephants" }],
  "segmentCount": 6,
  "wordCount": 39
}]

transcriptText is the whole thing as one string, ready for an LLM. segments keeps the timestamps if you need to link back to a moment in the video. Watch links, youtu.be links, Shorts, live and embed URLs and bare IDs all work.

Python: summarize a list of videos

# pip install apify-client
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

videos = [
    "https://www.youtube.com/watch?v=aircAruvnKk",
    "https://youtu.be/jNQXAC9IVRw",
]

run = client.actor("scrapemole/youtube-transcript-scraper").call(
    run_input={"videos": videos, "languages": ["en"]}
)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["status"] != "ok":
        print(item["videoId"], item["errorCode"], item["errorMessage"])
        continue
    text = item["transcriptText"]
    print(f'{item["title"]} ({item["wordCount"]} words)')
    # hand `text` to the model of your choice here, e.g. "Summarize in 5 bullets"

Two things worth handling:

  • Videos without captions come back as a row with status: "error" and errorCode: "NO_DATA", plus the list of languages that are available when there are some. They're not charged. If you need text for those, you need speech-to-text instead.
  • Other languages: pass "languages": ["es", "pt-BR"] to prefer certain tracks, or "translateTo": "en" to get YouTube's own translation. The row's language and isTranslated fields tell you what you got.

n8n: transcript into any workflow

You don't need a community node. Use the built-in HTTP Request node:

  • Method: POST
  • URL: https://api.apify.com/v2/acts/scrapemole~youtube-transcript-scraper/run-sync-get-dataset-items
  • Authentication: Header Auth, name Authorization, value Bearer YOUR_APIFY_TOKEN
  • Body (JSON): {"videos": ["{{ $json.videoUrl }}"], "languages": ["en"]}

Each output item is one video, so the next node (an OpenAI/Anthropic node, Notion, Google Sheets, a Slack message) can read {{ $json.transcriptText }} directly. A typical flow is: RSS feed of a channel, then this HTTP Request, then an LLM summary, then a post to your notes app. The same request works in Make's HTTP module.

If you were already calling another transcript Actor such as pintostudio/youtube-transcript-scraper from n8n or Make, you can keep your existing input (videoUrl, targetLanguage) and just change the Actor ID.

What it costs

You pay per transcript delivered: $0.004 on Apify's free plan, down to $0.002 on higher plans, so 1,000 transcripts is $2 to $4. Proxies are included, and failed, private or caption-less videos aren't charged. You can set a max cost per run and it stops cleanly at that limit.

Questions or a video that doesn't work? Open an issue on the Actor page or email us and I'll look at it.