How to use YouTube Scraper
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$3.60 per 1,000 videos on the Free plan, down to $2.16 on higher plans (live Apify price on 2026-10-31).
1. In the Apify Store (no code)
- Open arcadium/youtube-videos and sign in to Apify (a free account works).
- Fill in the input form. The fields below are the ones the example uses.
- Set a maximum cost per run in the run options if you want a hard cap.
- Start the run. Rows appear in the Output tab as they arrive.
- Export the dataset as JSON, CSV or Excel, or connect an integration.
| Input field | Type | Description |
|---|---|---|
urls | array | Example: https://www.youtube.com/@kurzgesagt (add /shorts or /streams for those tabs), @handle, a channel id (UC...), a playlist URL (https://www.youtube.com/playlist?list=...), a video or Shorts URL, a hashtag page (https://www.youtube.com/hashtag/cooking, add /shorts for Shorts only), a bare video id (dQw4w9WgXcQ) or a search page URL (https://www.youtube.com/results?search_query=lofi). Channels give up to maxResults videos each, newest first by default. |
searchQueries | array | YouTube search terms, e.g. ["web scraping tutorial", "iphone 17 review"]. Each term gives up to maxResults videos in YouTube's search order (position is the rank). Can be combined with urls. |
hashtags | array | YouTube hashtags, e.g. ["#cooking", "dancechallenge"] (letters, digits and _ only). Each gives up to maxResults videos from the hashtag page; with contentType shorts only its Shorts. Can be combined with urls and searchQueries. |
contentType | string | Values: videos (default), shorts. With shorts, search terms return Shorts only and hashtags read their Shorts tab. Channels: use maxResultsShorts or a /shorts channel URL. Full Shorts rows add commentCount, soundTitle and soundArtist and are charged as the short event. |
maxResults | integer | Videos per channel videos tab, playlist or search term, up to 10,000 (default 50). Videos outside the date range, private or members-only videos do not count. Direct video URLs are not limited by this. |
Example input:
{
"urls": [
"https://www.youtube.com/@kurzgesagt"
],
"searchQueries": [],
"hashtags": [],
"contentType": "videos",
"maxResults": 10
}
2. Over the Apify API
The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).
curl -X POST "https://api.apify.com/v2/acts/arcadium~youtube-videos/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.youtube.com/@kurzgesagt"],"searchQueries":[],"hashtags":[],"contentType":"videos","maxResults":10}'
3. From JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/youtube-videos').call({
"urls": [
"https://www.youtube.com/@kurzgesagt"
],
"searchQueries": [],
"hashtags": [],
"contentType": "videos",
"maxResults": 10
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
4. From Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/youtube-videos").call(run_input={
"urls": ["https://www.youtube.com/@kurzgesagt"],
"searchQueries": [],
"hashtags": [],
"contentType": "videos",
"maxResults": 10,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
5. In n8n
Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.
{
"nodes": [
{
"name": "Run arcadium/youtube-videos",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~youtube-videos/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"urls\":[\"https://www.youtube.com/@kurzgesagt\"],\"searchQueries\":[],\"hashtags\":[],\"contentType\":\"videos\",\"maxResults\":10}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}
6. From an AI assistant (MCP)
Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/youtube-videos",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}
More on the MCP page.
7. From LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/youtube-videos")
rows = tool.invoke({"run_input": {
"urls": ["https://www.youtube.com/@kurzgesagt"],
"searchQueries": [],
"hashtags": [],
"contentType": "videos",
"maxResults": 10,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])
Sample output
One row, from the example in the actor’s README. Long text is shortened and lists are cut to two items.
{
"rowType": "video",
"platform": "youtube",
"videoId": "dQw4w9WgXcQ",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
"channelName": "Rick Astley",
"channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
"channelHandle": "@RickAstleyYT",
"channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
"publishDate": "2009-10-24T23:57:33-07:00",
"publishDateIsApprox": false,
"publishedText": null,
"durationSeconds": 213,
"viewCount": 1829080503,
"likeCount": 19491607,
"commentCount": null,
"category": "Music",
"isShort": false,
"isLive": false,
"isLiveContent": false,
"isUpcoming": false,
"isUnlisted": false,
"isFamilySafe": true,
"keywords": [
"rick astley",
"Never Gonna Give You Up"
],
"hashtags": [
"rickastley"
],
"soundTitle": null,
"soundArtist": null,
"description": "The official video for “Never Gonna Give You Up” by Rick Astley. ...",
"thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
"thumbnailMaxUrl": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/maxresdefault.webp",
"captionLanguages": [
{
"code": "en",
"name": "English",
"isAutoGenerated": false
},
{
"code": "en",
"name": "English",
"isAutoGenerated": true
}
],
"hasCaptions": true,
"detailLevel": "full",
"source": null,
"sourceType": "video",
"position": null,
"scrapedAt": "2026-10-22T08:00:00.000Z"
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. Values: 'video': one video with its metadata (charged). 'error': a URL or video gave no data; see code and message (free). |
platform | Always 'youtube'. |
videoId | YouTube video id, 11 characters, e.g. dQw4w9WgXcQ. |
url | Watch page link of the video (Shorts included). |
title | Video title. |
channelName | Name of the channel that published the video (the publisher, not a viewer). |
channelId | Channel id, starts with UC, e.g. UCuAXFkgsw1L7xaCfnd5JJOw. |
channelHandle | Channel handle with the @, e.g. @RickAstleyYT. Null if YouTube did not return it. |
channelUrl | Link to the channel page built from channelId. |
publishDate | Publish date. Full rows: ISO 8601 with YouTube's offset, e.g. 2009-10-24T23:57:33-07:00. Fast-mode rows: YYYY-MM-DD estimated from the relative date (publishDateIsApprox true). |
publishDateIsApprox | True when publishDate was estimated from a relative date such as '3 months ago' (fast mode); false for exact dates. |
publishedText | Relative date as the listing shows it, e.g. '3 days ago', 'Streamed 2 weeks ago'. Null for direct video URLs. |
durationSeconds | Video length in seconds. Null for running live streams and some Shorts in fast mode. |
viewCount | Views at the time of the run. Exact on full rows, rounded (3.5M) on fast-mode rows. |
likeCount | Likes at the time of the run; null when hidden or in fast mode. |
commentCount | Comments at the time of the run, exact. Shorts in full mode always; regular videos when includeCommentCount is on; null otherwise. 0 when comments are off. |
category | YouTube category, e.g. Music, Education, News & Politics. Null in fast mode. |
isShort | True for YouTube Shorts. Null when unknown (fast mode outside the Shorts tab and search). |
isLive | True while the video is a live stream running now. |
isLiveContent | True for live streams and recorded live streams. Null in fast mode. |
isUpcoming | True for a scheduled premiere or live stream that has not started. |
isUnlisted | True for unlisted videos (reachable by link only). Null in fast mode. |
isFamilySafe | YouTube's family-safe flag. Null in fast mode. |
keywords | Tags the creator set on the video (may be empty). Null in fast mode. |
hashtags | #hashtags in the title and description, lower-cased, without the #. Null in fast mode. |
soundTitle | Shorts: the sound or song the Short uses, as YouTube's sound link shows it (e.g. a song title or 'Original sound'). Null for regular videos, Shorts without a sound link and fast mode. |
soundArtist | Shorts: the artist or channel credited for the sound (the part after the dot on the sound link). Null when not shown. |
description | Video description, full or short per input; emails and phone numbers are removed. Null in fast mode. |
thumbnailUrl | Thumbnail image, 480x360 (hqdefault), exists for every video. |
thumbnailMaxUrl | Largest thumbnail (maxresdefault, 1280x720 or more) when the upload has one; null otherwise or in fast mode. |
captionLanguages | Every caption track of the video: code (e.g. en, de-DE), name, isAutoGenerated. Empty list: no captions. Null when not requested, in fast mode, or YouTube did not answer. |
hasCaptions | True when the video has at least one caption track; null when captionLanguages is null. |
detailLevel | 'full': read from the video's player data (exact date, likes, category). 'listing': only what the channel, playlist or search listing shows (fast mode). |
source | The channel tab, playlist URL or search term this video came from; null for videos given directly. |
sourceType | Values: 'video' (given directly), 'channel', 'playlist', 'search', 'hashtag'. |
position | 1-based position in the source list (search rank, playlist index, channel order); null for direct videos. |
scrapedAt | When the row was read, ISO 8601. |
input | Error rows: the URL, id or search term as given. |
code | Error rows: not_found (private, removed, unknown channel or playlist), no_match (nothing found for the input), unavailable (age-restricted, members-only, temporary error), blocked (rate limited, retry later), invalid_input. |
message | Error rows: what happened, in plain English. |
Schedule it
In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.