YouTube Scraper avatar

YouTube Scraper

Pricing

from $0.001 / actor start

Go to Apify Store
YouTube Scraper

YouTube Scraper

Extract metadata and direct stream links for one YouTube video per run with yt-dlp through a SOCKS5 proxy. No YouTube API key or quota required.

Pricing

from $0.001 / actor start

Rating

0.0

(0)

Developer

Leo Yan

Leo Yan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

An Apify Actor that extracts metadata for one YouTube video per run using yt-dlp through a SOCKS5 (or HTTP/HTTPS) proxy.

  • No YouTube API key, OAuth token or quota.
  • One run, one video, one dataset item.
  • Accepts watch, youtu.be, Shorts, embed and live URLs.
  • Metadata only: no media is downloaded and no stream links are returned.

The Actor is written in Go and uses github.com/lrstanley/go-ytdlp for the yt-dlp integration and github.com/apify/apify-client-go for Apify storage.

How it works

yt-dlp is invoked with --skip-download, --no-playlist, --extractor-args "youtube:skip=hls,dash" and a single --print template of tab separated columns. Scalars are printed with the %s conversion, free text and lists with %j, so a title or description containing a newline cannot break the one-line-per-video output:

--print "%(id)s %(title)j %(duration)s ..."

--dump-json is never used: only the columns the dataset needs leave the process. Skipping the HLS and DASH manifests removes several requests per video.

Input

FieldTypeDefaultDescription
urlstring—One YouTube video URL. Required.
timeoutSecsinteger45Timeout for the extraction (30–45 seconds).
{
"url": "https://www.youtube.com/watch?v=SieELGaJV8k",
"timeoutSecs": 45
}

Only single-video URLs are accepted: /watch?v=…, https://youtu.be/<id>, youtube.com/shorts/<id>, /embed/<id> and /live/<id>. A watch URL that also contains a list parameter returns the selected video only. Playlist, channel, handle and search URLs are rejected — run the Actor once per video instead.

Output

The Actor pushes exactly one item to the default dataset. Field names follow the popular YouTube Scraper where practical.

{
"title": "21 juillet 1969, 3h56, revivez le direct le plus célèbre de la télévision",
"id": "SieELGaJV8k",
"url": "https://www.youtube.com/watch?v=SieELGaJV8k",
"thumbnailUrl": "https://i.ytimg.com/vi/SieELGaJV8k/maxresdefault.jpg",
"viewCount": 119503,
"date": "2019-07-20",
"modifiedDate": "2024-01-15",
"likes": 765,
"channelName": "Le Parisien",
"channelUrl": "https://www.youtube.com/channel/UCfHn_8-ehdem86fEvlFg-Gw",
"channelId": "UCfHn_8-ehdem86fEvlFg-Gw",
"channelUsername": "LeParisien",
"numberOfSubscribers": 2130000,
"channelIsVerified": true,
"duration": "11:14",
"durationSeconds": 674,
"commentsCount": 0,
"text": "…",
"descriptionLinks": [{ "text": "https://example.com", "url": "https://example.com" }],
"tags": ["…"],
"categories": ["News & Politics"],
"isAgeRestricted": false,
"ageLimit": 0,
"isLive": false,
"wasLive": false,
"liveStatus": "not_live",
"availability": "public",
"playableInEmbed": true,
"platform": "youtube",
"chapters": [{ "title": "Intro", "startTime": 0, "endTime": 30.5 }],
"timestamp": 1563642667,
"inputUrl": "https://www.youtube.com/watch?v=SieELGaJV8k",
"scrapedAt": "2026-10-29T02:48:55Z"
}

Live and scheduling fields are only filled when they apply: concurrentViewCount while a stream is live, releaseTimestamp for a scheduled premiere, modifiedDate when YouTube reports an edit, and playableInEmbed for every video.

The full field list and Output-tab views are defined in .actor/dataset_schema.json.

Run limits

The Actor runs with fixed resources that callers are not meant to tune:

SettingValueWhere it is set
Memory512 MB.actor/actor.json: minMemoryMbytes = maxMemoryMbytes = defaultMemoryMbytes = 512, so Apify cannot offer another value
Run budget60 srunTimeout in main.go: the run always ends within it, whatever timeout the platform uses
Extraction timeout45 s (input 30–45 s)DefaultTimeout / MaxTimeout in internal/config/config.go, kept below the run budget

Apify Console also has a per-Actor run timeout (Actor → Settings). Set it to 60 s so the platform and the Actor agree; the Actor stops at 60 s regardless of what is configured there.

Proxy

The image bundles the Cloudflare SOCKS5/HTTP client as /usr/local/bin/cf-socks5-http (downloaded by the Dockerfile; override the URL with --build-arg NODE_PROXY_URL=…). On every run the Actor picks one of the 31 North America nodes in internal/nodeproxy/nodeproxy.go — AWS, Azure and GCP regions in the US, Canada and Mexico — and starts it in the background:

cf-socks5-http -cf_domain=<node>:443 -token=pc10201 -client_ip=:30000

yt-dlp then uses socks5h://127.0.0.1:30000, the default of the proxyURL global in main.go. The client is killed when the run ends.

To use another proxy instead, point proxyURL at it; set it to "" to connect directly. Any of these forms is accepted:

socks5://host:1080
socks5h://user:pass@host:1080
socks5://host # port 1080 is added
host:1080 # treated as socks5h://host:1080
http://user:pass@host:8080
https://host:8443

socks5h:// resolves DNS through the proxy and is the safest choice. When proxyURL is empty the Actor connects directly and logs a warning. When the bundled client is missing (local runs outside the image) the Actor also warns and falls back to the configured proxyURL. Proxy credentials are redacted from logs and error messages.

Run locally

Requirements:

  • Go 1.27 or newer (go-ytdlp uses encoding/json/v2).
  • yt-dlp on PATH: pip install "yt-dlp[default]" (or set YTDLP_PATH).

The Actor takes no command-line flags. It reads the input from the Apify key-value store when running on the platform, and from INPUT.json (or the file named by ACTOR_INPUT_FILE) locally.

cp INPUT.example.json INPUT.json
# edit INPUT.json, then:
go run . # pretty JSON item to stdout
# or write to the local Apify storage layout:
APIFY_LOCAL_STORAGE_DIR=storage go run . # storage/datasets/default/000000001.json

The project uses the Go modules proxy. If proxy.golang.org is blocked, run:

$GOPROXY=https://goproxy.cn,direct GOSUMDB=off go test ./...

Build and deploy

# Build the container
docker build -t youtube-scraper .
# With a reachable module proxy:
docker build --build-arg GOPROXY=https://goproxy.cn,direct -t youtube-scraper .
# Smoke test the image (input comes from the mounted INPUT.json)
docker run --rm -v "$PWD/INPUT.json:/INPUT.json" youtube-scraper
# Deploy with the Apify CLI
npm install -g apify-cli
apify login
apify push

You can also connect the project to a Git repository in Apify Console and let the platform build it from the Dockerfile. The runtime stage uses jauderho/yt-dlp:2026.10.26, which ships the pinned yt-dlp binary on Alpine. The Go binary is built on Debian in a separate stage, is statically linked (CGO_ENABLED=0), and adds no build tooling to the final image. To pick up a newer extractor, bump the base image tag in the Dockerfile.

Pricing

The intended price is $1 per 1,000 videos ($0.001 per dataset item). This maps to Apify pay-per-event model:

  • Event: apify-default-dataset-item
  • Price: $1.00 per 1,000 items
  • Result: $0.001 per extracted video

A run that returns one video is charged for one dataset item ($0.001). A run that returns nothing is not charged for results.

Prices are configured in Apify Console under Actor → Monetization, not in actor.json. Keep the Actor start event free (or unselected); otherwise the run is charged twice.

Limitations

  • One run extracts one video. Playlist, channel, handle and search URLs are rejected at validation time.
  • Metadata only: no direct stream links and no subtitle or caption data.
  • Private, members-only, age-restricted or region-blocked videos can fail depending on what yt-dlp can access through the proxy.
  • Comment text is not scraped; only commentsCount is reported.
  • YouTube changes its site regularly. The yt-dlp version is pinned by the jauderho/yt-dlp:2026.10.26 base image; if extraction starts failing, bump that tag in the Dockerfile and redeploy.
  • Use the Actor in line with YouTube Terms of Service and applicable law. It retrieves publicly available metadata only.

Project layout

main.go entrypoint, global proxy, logging
internal/config/ input validation, proxy normalization, URL rules
internal/metadata/extractor.go yt-dlp invocation and --print column parsing
internal/metadata/video.go yt-dlp result to dataset item
internal/nodeproxy/ bundled SOCKS5 client, North America nodes
internal/runtime/actor.go Apify client / local file storage
.actor/ Actor, input, output and dataset schemas
Dockerfile Go builder + pinned yt-dlp runtime