YouTube Video Subtitles Scraper With Engagement Filters
Pricing
from $2.99 / 1,000 results
YouTube Video Subtitles Scraper With Engagement Filters
YouTube Video Subtitles Scraper — Scrape YouTube videos with subtitles, transcripts, titles, channels, views, likes, comments, and engagement metrics. Filter videos by engagement thresholds to discover high-performing content and analyze trends, topics, and audience interest.
Pricing
from $2.99 / 1,000 results
Rating
0.0
(0)
Developer
SimpleAPI
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
YouTube Subtitles Scraper — Transcripts, Views and Filters
Extract timestamped YouTube subtitles and transcripts in bulk, with views, likeCount, commentCount, engagementRate, publishedAt, and videoAgeDays on every row. This YouTube transcript scraper adds pre-fetch engagement, date, and duration filters — a video that doesn't clear your bar is reported as filtered_out and its caption request is never made. Researchers, content teams, and data pipelines use it to pull transcripts only from videos worth transcribing, without downloading media files. Point it at a list of URLs and get structured JSON back.
What is YouTube Subtitles Scraper?
YouTube Subtitles Scraper is an Apify Actor that fetches a video's timestamped caption track (manual captions when the creator uploaded them, YouTube's auto-generated captions otherwise) alongside its public metadata, and returns one JSON row per video. No YouTube account, Google Cloud project, or API key is required — it reads the same public video page and caption track a signed-out viewer's browser would.
Key features:
- 🎬 URL-based input — paste full
watch?v=,youtu.be/, or/shorts/links, one per line - 📝 Timestamped transcripts — an array of
{start, dur, text}segments per video, manual captions preferred, auto-generated as fallback - 📊 Real engagement numbers —
views,likeCount,commentCount, plus derivedengagementRateandvideoAgeDays, honestlynullwhen YouTube hides the count rather than a faked0 - 🗓️ Absolute dates —
publishedAtconverted to ISO 8601 from YouTube's raw upload date, and a freshscrapedAtper row - ⚙️ Pre-fetch filters — minimum views/likes/comments, a publish-date window, and a duration range that skip the (costlier) caption request entirely for videos that don't qualify
- 🔁 Legacy key compatibility —
startUrls,language, andmaxWorkersare still accepted as aliases
What data can I extract with YouTube Subtitles Scraper?
Every processed URL produces one row with the following fields, whether the video's captions were retrieved, filtered out, or failed.
| Field | Example Value | Use Case |
|---|---|---|
url | "https://www.youtube.com/watch?v=dQw4w9WgXcQ" | The input video URL |
title | "Rick Astley - Never Gonna Give You Up" | Video title |
channel_name | "Rick Astley" | Channel display name |
channelId | "UCuAXFkgsw1L7xaCfnd5JJOw" | Canonical channel identifier |
channelUrl | "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw" | Channel URL |
publishedAt | "2009-10-25T00:00:00Z" | Upload date, absolute ISO 8601 (converted from YouTube's raw upload date) |
videoAgeDays | 6136 | Days between publishedAt and the moment this row was finalized |
length | "3:33" | Duration, M:SS or H:MM:SS |
views | 1600000000 | View count, null if unavailable |
likeCount | 18000000 | Like count, null if hidden by the creator |
commentCount | 2500000 | Comment count, null if hidden or comments disabled |
engagementRate | 0.012813 | (likeCount + commentCount) / views, rounded to 6 decimals, null if views is falsy |
status | "success" | Row outcome — success, no_transcript, fetch_error, filtered_out, invalid_input, or temporary_issue |
filterReason | null | Which pre-fetch filter excluded the video, e.g. "views below the configured minimum of 100000"; null unless status is filtered_out |
transcript | [{"start": "0.240", "dur": "3.360", "text": "We're no strangers to love"}] | Timestamped caption segments; empty array for filtered, failed, or captionless videos |
description | "The official video for..." | Video description, null if unavailable |
tags | ["Rick Astley", "Never Gonna Give You Up"] | Video tags, null if unavailable |
message | "Transcript loaded (manual captions)." | Human-readable status message for this row |
success | true | true only when transcript segments were actually retrieved |
type | "video" | Row type, always "video" |
scrapedAt | "2026-08-15T12:00:00Z" | When this specific row was finalized, ISO 8601 |
📈 Engagement and timing data
views, likeCount, commentCount, engagementRate, and videoAgeDays are fetched and attached to every row — including filtered_out rows — because video metadata is retrieved before any filter is checked. This lets you compare a video's engagement against the filter thresholds you set, or build an engagement-vs-time distribution across a batch of URLs without re-running anything. engagementRate is null whenever views is 0 or unavailable, since the ratio can't be computed.
🎯 Filter results and segmentation fields
status and filterReason tell you exactly why a row looks the way it does — filter it downstream on status == "filtered_out" to see excluded videos and their reason, or status == "success" to keep only rows with a retrieved transcript. tags and channelId let you group results by topic or channel; length and videoAgeDays let you segment by duration band or recency without re-parsing dates.
How does YouTube Subtitles Scraper differ from the official YouTube Data API?
The YouTube Data API v3's captions.download method — Google's only endpoint for exporting caption text — requires OAuth 2.0 authorization from the video's owner: per Google's own documentation, "this method requires the user to have permission to edit the video," so it cannot return caption text for a video you don't own, regardless of quota (developers.google.com/youtube/v3/docs/captions/download, checked 2026-08-15).
| Feature | YouTube Data API v3 | YouTube Subtitles Scraper |
|---|---|---|
| Caption text for a video you don't own | ❌ Not obtainable — captions.download requires OAuth with edit permission on that specific video | ✅ Reads the same public caption track any viewer's player loads, for any video URL |
| Setup | Google Cloud project, API key, OAuth 2.0 consent flow for captions | Apify account and a list of video URLs |
| Caption quota | 200 units per captions.download call, against a default 10,000 units/day per project (developers.google.com, checked 2026-08-15) | Apify pay-per-event charging, no Google quota |
| Video metadata (views/likes/comments/duration) | ✅ via videos.list, API key only | ✅ included in every row |
| Pre-fetch engagement/date/duration filtering | Not built in — implement client-side after fetching | ✅ minViews/minLikes/minComments/duration/date filters built in |
| Skips the caption request for non-qualifying videos | Not applicable | ✅ filtered_out rows never trigger the caption fetch |
Why can't I just use the YouTube Data API to download captions?
captions.download is the only official way to get caption text, but Google scopes it to the video's own owner: the request needs OAuth 2.0 authorization for an account with edit permission on that exact video, and costs 200 quota units against a project's default 10,000-unit daily allowance (developers.google.com, checked 2026-08-15). That rules it out for pulling captions from videos you don't control — competitor content, research corpora spanning many channels, or any third-party video. This Actor instead reads the same public caption tracks (manual or auto-generated) that YouTube's own player loads for a signed-out viewer, so no OAuth grant or channel ownership is needed.
Use the official API if you manage the channel and only need captions for your own uploads. Use this Actor when you need transcript text from videos you don't own.
How to use data extracted from YouTube video transcripts?
🔬 Researchers and NLP/dataset teams
Build a transcript corpus filtered to a minimum popularity or a recent publish window — set minViews, minLikes, and publishedAfter (e.g. "365 days"), then pull transcript and engagementRate from every row that passes. Filtered-out videos still return their metadata, so you can see exactly what was excluded and why (filterReason) without re-running the batch.
🏢 Agencies auditing channel back catalogues
Run every video URL from a client's or competitor's channel and filter the output on status == "no_transcript" to find accessibility gaps in their caption coverage, or on status == "success" with a low engagementRate to flag underperforming uploads worth a messaging review.
📊 Market research and content intelligence
Sweep a set of video URLs across a niche and correlate transcript topics against engagementRate, views, and videoAgeDays to see what content and phrasing actually drives engagement, without watching each video by hand.
🤖 AI agents and automated pipelines
Feed transcript segments into a RAG index or summarization pipeline keyed by url, or chain the Actor into a scheduled Apify run that turns a fresh list of video URLs into filtered transcript-plus-metrics data automatically.
🔼 Input sample
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
youtubeUrls | No | array | Add full YouTube video URLs, one per line (example: https://www.youtube.com/watch?v=VIDEO_ID or https://youtu.be/VIDEO_ID). Also accepts https://www.youtube.com/shorts/VIDEO_ID. | ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] |
startUrls | No | array | Legacy alias of youtubeUrls, kept for input compatibility with the base actor. Use youtubeUrls above for new runs. | ["https://www.youtube.com/watch?v=..."] |
preferredCaptionLanguage | No | string | Language code to prefer for captions (examples: en English, es Spanish, fr French). Falls back automatically to any available track when the preferred language isn't found. Default: "en". | "en" |
language | No | string | Legacy alias of preferredCaptionLanguage, kept for input compatibility with the base actor. | "en" |
minViews | No | integer | Skip videos with fewer than this many views. 0 = no view filter. Minimum: 0. Default: 0. | 100000 |
minLikes | No | integer | Skip videos with fewer than this many likes (or with a hidden like count). 0 = no like filter. Minimum: 0. Default: 0. | 5000 |
minComments | No | integer | Skip videos with fewer than this many comments (or with comments disabled/hidden). 0 = no comment filter. Minimum: 0. Default: 0. | 100 |
publishedAfter | No | string | Only process videos published on or after this date. Accepts an absolute date or a relative one (e.g. '30 days'). Leave empty to skip. | "2020-01-01" |
publishedBefore | No | string | Only process videos published on or before this date. Accepts an absolute date or a relative one. Leave empty to skip. | "30 days" |
minDurationSeconds | No | integer | Skip videos shorter than this many seconds. 0 = no minimum-duration filter. Minimum: 0. Default: 0. | 60 |
maxDurationSeconds | No | integer | Skip videos longer than this many seconds. 0 = no maximum-duration filter. Minimum: 0. Default: 0. | 1200 |
maxConcurrentRequests | No | integer | Number of videos processed in parallel (1-10). Higher values finish a batch faster. Minimum: 1. Maximum: 10. Default: 4. | 4 |
maxWorkers | No | integer | Legacy alias of maxConcurrentRequests, kept for input compatibility with the base actor. Minimum: 1. Maximum: 10. | 4 |
proxyConfiguration | No | object | Configure proxy preferences for better reliability when needed. Leave default settings for normal use. Default: {"useApifyProxy": false}. | {"useApifyProxy": false} |
{"youtubeUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ","https://www.youtube.com/watch?v=jNQXAC9IVRw"],"preferredCaptionLanguage": "en","minViews": 100000,"publishedAfter": "2020-01-01","maxConcurrentRequests": 4}
Common pitfall: all configured filters must pass together — minViews, minLikes, minComments, the duration range, and the date window are combined with AND logic, not OR. Setting minViews: 100000 and minLikes: 5000 excludes a video with 200,000 views but only 3,000 likes. Leave a filter at its default 0 (or empty, for the date fields) to remove it from the check entirely rather than trying to disable it with a low value.
🔽 Output sample
Every run produces typed, normalized JSON — one row per input URL, always pushed, whatever the outcome. Export as JSON, CSV, Excel, or any other format the Apify dataset export supports.
{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","title": "Rick Astley - Never Gonna Give You Up","channel_name": "Rick Astley","channelId": "UCuAXFkgsw1L7xaCfnd5JJOw","channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw","publishedAt": "2009-10-25T00:00:00Z","videoAgeDays": 6136,"length": "3:33","views": 1600000000,"likeCount": 18000000,"commentCount": 2500000,"engagementRate": 0.012813,"status": "success","filterReason": null,"transcript": [{ "start": "0.240", "dur": "3.360", "text": "We're no strangers to love" }],"description": "The official video for \"Never Gonna Give You Up\" by Rick Astley.","tags": ["Rick Astley", "Never Gonna Give You Up", "80s"],"message": "Transcript loaded (manual captions).","success": true,"type": "video","scrapedAt": "2026-08-15T12:00:00Z"}
A video that fails a configured filter is still returned in full, without a wasted caption request:
{"url": "https://www.youtube.com/watch?v=...","views": 4200,"likeCount": 90,"status": "filtered_out","filterReason": "views below the configured minimum of 100000","transcript": [],"success": false,"type": "video"}
⚠️ Every row pushed — success, no_transcript, fetch_error, filtered_out, invalid_input, and temporary_issue alike — is charged one row_result event. Pre-fetch filtering skips the second, costlier network call (the caption fetch) for videos that don't qualify, which saves run time, but it does not exempt that row from being charged. Check the Actor's Pricing tab on the Apify Console for current per-event pricing.
How do you filter and target specific videos?
There is no keyword or category search here — targeting starts with an exact list of video URLs in youtubeUrls, so scope precision is entirely in your hands: process exactly the videos you list, nothing discovered or guessed. From there, three filter axes narrow which videos get their captions fetched at all. Quality thresholds — minViews, minLikes, minComments — skip low-engagement videos before the caption request fires. Time and duration bands — publishedAfter/publishedBefore (absolute date or a relative expression like "30 days") and minDurationSeconds/maxDurationSeconds — scope by recency or length. Volume has no schema-imposed cap: one row is produced per URL you supply, and maxConcurrentRequests (1–10) only controls how many videos are processed in parallel, not how many total. Remember all active filters combine with AND logic, so a video must clear every threshold you set, not just one.
{ "youtubeUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "minViews": 500000, "minLikes": 10000 }
{ "youtubeUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "publishedAfter": "30 days" }
{ "youtubeUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "minDurationSeconds": 180, "maxDurationSeconds": 1200 }
▶️ Want to try other YouTube scrapers?
| Scraper Name | What it extracts |
|---|---|
| Youtube Video Summarizer With Multi-Language Transcripts | YouTube video transcripts and summaries in multiple languages |
| Facebook Video Transcript Extractor By Keyword Search | Facebook video transcripts, discoverable by keyword or Page |
| TikTok Scraper With Repost Insights Data | TikTok videos with repost insight data |
| Instagram Post Details Scraper With Engagement Data | Instagram post details and engagement metrics |
How to extract YouTube data programmatically
YouTube Subtitles Scraper runs as a standard Apify Actor: one API call starts the run, and the finished dataset comes back as structured JSON over the Apify API.
🐍 Python example
from apify_client import ApifyClientclient = ApifyClient("<APIFY_API_TOKEN>")run = client.actor("your-username/youtube-video-subtitles-scraper-with-engagement-filters").call(run_input={"youtubeUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"minViews": 100000,"preferredCaptionLanguage": "en",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["title"], item["views"], item["status"])
🔌 MCP for AI agents
This Actor is reachable through Apify's Actors MCP Server at https://mcp.apify.com, whose call-actor tool can invoke any Apify Actor by name without pre-configuration, over either OAuth or a bearer APIFY_API_TOKEN (docs.apify.com/platform/integrations/mcp, checked 2026-08-15), for MCP clients including Cursor, VS Code, Claude Desktop, and the Apify CLI's client integrations. An agent can call it with a video URL to pull a fresh transcript and engagement snapshot as grounding context mid-conversation, without a separate integration.
📊 Export to spreadsheets or CRM
Export the dataset as CSV or Excel directly from the Apify Console or API, mapping url → a link column, title/channel_name → identifying columns, transcript → a text/notes column, and views/likeCount/commentCount/engagementRate → metric columns for import into a spreadsheet or CRM.
⚖️ Is it legal to scrape YouTube video subtitles?
Scraping publicly available YouTube video pages is generally legal — this Actor only reads what YouTube already serves to a signed-out viewer, the same page a browser would load. That said, video transcripts and titles can contain personal data: names, opinions, or other identifiable information spoken or written by the people in the video, and channel_name/channelId identify the channel itself, which is often an individual creator. If you store or reuse that data, GDPR and CCPA obligations around lawful basis, retention, and deletion apply just as they would to any other personal information you collect. Consult legal counsel for commercial applications involving bulk storage of personal data.
❓ FAQ
What happens to a video that's private, deleted, or region-locked?
It's returned with status: "temporary_issue" and the message "This video's information could not be retrieved after multiple attempts." The Actor does not distinguish a permanently unavailable video from a transient connection failure — both surface with the same status and message, since the underlying fetch fails the same way in either case.
Can I get engagement data along with the transcript?
Yes — views, likeCount, commentCount, engagementRate, and videoAgeDays are fetched and attached to every row, including filtered_out rows, because metadata is retrieved before any filter is checked. A count comes back null, not 0, when YouTube hides it.
How accurate is the engagement data?
The Actor returns whatever view/like/comment counts YouTube reports to a signed-out viewer at request time — not a cached or estimated number. Accuracy depends on how current YouTube's own public counts are for that video at the moment of the run. For time-sensitive comparisons, re-run rather than reuse an older result.
How many videos can I get per run?
There's no maxItems cap in the input schema — one row is produced per URL you supply in youtubeUrls (or startUrls). maxConcurrentRequests (1–10, default 4) only controls how many videos are processed in parallel, not the total count.
Do the engagement, date, and duration filters have to all pass, or just one?
All configured filters must pass together (AND logic). A video failing any single active filter is reported filtered_out with the specific filterReason naming that filter; leave a filter at 0 (or empty for the date fields) to remove it from the check.
Does YouTube Subtitles Scraper work with Claude, ChatGPT, and AI agent frameworks?
Yes — it's reachable through Apify's Actors MCP Server (https://mcp.apify.com) for MCP-compatible clients, and callable as a standard HTTP/API endpoint via apify_client from any agent framework that can make an API call.
How does this compare to other YouTube caption scrapers?
We didn't verify specific claims from other YouTube caption scrapers' live listings for this README. What's verifiable from this Actor's own source: pre-fetch engagement/date/duration filtering that skips the caption request for videos that don't qualify, absolute (not relative) publish dates on every row, and legacy input-key compatibility (startUrls, language, maxWorkers) so existing input JSON keeps working.
Can I use this without a YouTube API key or Google developer account?
Yes. The Actor needs no YouTube Data API key, OAuth consent, or Google Cloud project — only an Apify account and the video URLs to process. It reads public caption tracks the same way a signed-out viewer's browser does, not the official captions.download endpoint.
Can I still use the legacy startUrls, language, and maxWorkers input keys?
Yes — all three are accepted as aliases of youtubeUrls, preferredCaptionLanguage, and maxConcurrentRequests, so input JSON built for the base actor keeps working unchanged.
Conclusion
YouTube Subtitles Scraper turns a list of YouTube video URLs into timestamped transcripts plus real engagement and date metadata, filtering out videos that don't meet your view, like, comment, date, or duration thresholds before their captions are ever requested. It's built for researchers and NLP teams assembling filtered transcript corpora, agencies auditing channel caption coverage, market researchers correlating content against engagement, and AI pipelines that need transcript text as grounding context. Start a run from the Apify Console or call it over the API with apify_client to get typed JSON transcripts back.