Arcadium Data Scrapers
Home / Guides / YouTube Scraper

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)

  1. Open arcadium/youtube-videos and sign in to Apify (a free account works).
  2. Fill in the input form. The fields below are the ones the example uses.
  3. Set a maximum cost per run in the run options if you want a hard cap.
  4. Start the run. Rows appear in the Output tab as they arrive.
  5. Export the dataset as JSON, CSV or Excel, or connect an integration.
Input fieldTypeDescription
urlsarrayExample: 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.
searchQueriesarrayYouTube 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.
hashtagsarrayYouTube 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.
contentTypestringValues: 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.
maxResultsintegerVideos 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"
}
FieldWhat it holds
rowTypeKind 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).
platformAlways 'youtube'.
videoIdYouTube video id, 11 characters, e.g. dQw4w9WgXcQ.
urlWatch page link of the video (Shorts included).
titleVideo title.
channelNameName of the channel that published the video (the publisher, not a viewer).
channelIdChannel id, starts with UC, e.g. UCuAXFkgsw1L7xaCfnd5JJOw.
channelHandleChannel handle with the @, e.g. @RickAstleyYT. Null if YouTube did not return it.
channelUrlLink to the channel page built from channelId.
publishDatePublish 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).
publishDateIsApproxTrue when publishDate was estimated from a relative date such as '3 months ago' (fast mode); false for exact dates.
publishedTextRelative date as the listing shows it, e.g. '3 days ago', 'Streamed 2 weeks ago'. Null for direct video URLs.
durationSecondsVideo length in seconds. Null for running live streams and some Shorts in fast mode.
viewCountViews at the time of the run. Exact on full rows, rounded (3.5M) on fast-mode rows.
likeCountLikes at the time of the run; null when hidden or in fast mode.
commentCountComments 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.
categoryYouTube category, e.g. Music, Education, News & Politics. Null in fast mode.
isShortTrue for YouTube Shorts. Null when unknown (fast mode outside the Shorts tab and search).
isLiveTrue while the video is a live stream running now.
isLiveContentTrue for live streams and recorded live streams. Null in fast mode.
isUpcomingTrue for a scheduled premiere or live stream that has not started.
isUnlistedTrue for unlisted videos (reachable by link only). Null in fast mode.
isFamilySafeYouTube's family-safe flag. Null in fast mode.
keywordsTags 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.
soundTitleShorts: 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.
soundArtistShorts: the artist or channel credited for the sound (the part after the dot on the sound link). Null when not shown.
descriptionVideo description, full or short per input; emails and phone numbers are removed. Null in fast mode.
thumbnailUrlThumbnail image, 480x360 (hqdefault), exists for every video.
thumbnailMaxUrlLargest thumbnail (maxresdefault, 1280x720 or more) when the upload has one; null otherwise or in fast mode.
captionLanguagesEvery 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.
hasCaptionsTrue 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).
sourceThe channel tab, playlist URL or search term this video came from; null for videos given directly.
sourceTypeValues: 'video' (given directly), 'channel', 'playlist', 'search', 'hashtag'.
position1-based position in the source list (search rank, playlist index, channel order); null for direct videos.
scrapedAtWhen the row was read, ISO 8601.
inputError rows: the URL, id or search term as given.
codeError 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.
messageError 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.