YouTube Search Scraper
Pricing
$0.20 / 1,000 results
YouTube Search Scraper
Search YouTube and export results to JSON by keyword. No YouTube Data API key or quota. One record per result (video, channel, or playlist) with title, views, and channel. Filter by type, date, and duration. Queries that return nothing are free. $0.20 per 1,000 results.
Pricing
$0.20 / 1,000 results
Rating
0.0
(0)
Developer
apihq
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Search YouTube and export the results as clean JSON from a keyword. No YouTube Data API key, no quota, no browser.
The contract is simple: a query that finds nothing never fails your run, and you only pay for results delivered. Search a batch of keywords, filter by type, date, or duration, and get one row per result. Pay-per-result at $0.20 per 1,000 results. Queries that return nothing cost $0.
See one run
Input:
{"queries": ["lofi hip hop", "synthwave"],"type": "video","sortBy": "views","uploadDate": "month","duration": "long","maxResults": 5}
What comes back:
- Up to 5
success: truevideo rows per query, billed: the most-viewed long lofi and synthwave videos from the past month - Each row carries the video, its channel, views, and thumbnail
- Run status: SUCCEEDED
- Charged: up to 10 results = $0.002
query alone works too. Every knob above is optional: type (videos, channels, or playlists), sortBy (relevance, date, views, or rating), uploadDate (last hour through this year), and duration (short, medium, or long). A query that returns nothing is a free success: false row, so one miss never blocks the batch.
Why this Actor
- No YouTube Data API key or quota. Many search workflows lean on Google's official Data API, with its keys to provision, daily quota accounting, and per-call limits. This Actor reads YouTube's public search directly, so there is no key to provision and no quota to ration, and it returns as many results as YouTube serves for the query.
- You pay only for results. Billing fires on an explicit per-result event, not on dataset writes, so a query that returns nothing sits in your dataset for free. There is no per-run start fee, so an empty run costs zero.
- A bad filter never breaks the batch. An unusable filter value or a query that finds nothing returns a
success: falserecord with a machine-readablecode(NO_RESULTS,VALIDATION_FAILED,SEARCH_TIMEOUT,DEADLINE_EXCEEDED). Branch oncodewithout parsing English.
Best for
Data and AI workflows that turn a keyword into a dataset: competitor and market research, trend and topic discovery, tracking who ranks for a term, and collecting video IDs to feed transcript or comment jobs. The failure-free billing and machine-readable error rows matter most when the search runs unattended inside a larger pipeline, where one query with no results should never take down the batch. It works fine for one-off manual pulls too.
Input example
A single search, videos only:
{"query": "python tutorial","type": "video","maxResults": 100,"sortBy": "views","uploadDate": "month"}
A batch of keywords:
{"queries": ["lofi hip hop", "synthwave", "jazz for studying"],"type": "video","maxResults": 200}
query and queries are merged and de-duplicated. Up to 50 unique queries run per job. Each query is walked page by page until it reaches maxResults or YouTube runs out of results. Filters (type, sortBy, uploadDate, duration) apply to every query in the run.
Output example
One record per result. The search term is copied onto every row so each row stands alone. A video result:
{"success": true,"result_type": "video","search_query": "lofi hip hop","video_id": "n61ULEU7CO0","title": "Best of lofi hip hop 2021","url": "https://www.youtube.com/watch?v=n61ULEU7CO0","thumbnail_url": "https://i.ytimg.com/vi/n61ULEU7CO0/hqdefault.jpg","view_count": "55,765,840 views","published": "4 years ago","duration": 22258,"is_live": false,"description_snippet": "The best lofi hip hop songs of 2021, all in one mix.","channel_name": "Lofi Girl","channel_id": "UCSJ4gkVC6NrvII8umztf0Ow","channel_url": "https://www.youtube.com/@LofiGirl"}
A query that returns nothing lands in the same dataset and is not charged:
{"success": false,"search_query": "asdkjfhqwiuerypoxzcv","code": "NO_RESULTS","error": "Search returned no results for query: asdkjfhqwiuerypoxzcv","request_id": "req_911f37d2e55644ff9d9e4a3f","status_code": 404}
Split hits from misses on the success field, and the kind of hit on result_type. Quote request_id in any support issue and we can trace the exact request.
What you get
Every result is one record with success: true. result_type tells you which kind, and which fields the row carries:
| Field | Type | On which rows | What it is |
|---|---|---|---|
success | boolean | all | true for a result (billed), false for a query that returned nothing (not billed). |
result_type | string | all | video, channel, playlist, or shorts. |
search_query | string | all | The search term that produced this row. |
title | string | all | Video/playlist title, or the channel name. |
url | string | all | Canonical URL of the result (watch, shorts, channel, or playlist). |
thumbnail_url | string | all | URL of the largest available thumbnail. May be empty. |
video_id | string | video, shorts | The 11-character video ID. |
view_count | string | video, shorts | View count as a display string. May be empty. |
published | string | video | Relative publish string (for example 5 years ago). May be empty. |
duration | integer | video | Length in whole seconds. Absent for live streams. |
is_live | boolean | video | true for a live stream. |
description_snippet | string | video, channel | A short description excerpt. May be empty. |
channel_name | string | video, playlist | Uploader or owner name. |
channel_id | string | video, channel, playlist | The UC... channel ID, where available. |
channel_url | string | video | Uploader channel URL. May be empty. |
channel_handle | string | channel | The channel @handle. May be empty. |
subscriber_text | string | channel | Subscriber count as a display string (for example 506M subscribers). |
playlist_id | string | playlist | The playlist ID. |
video_count | string | playlist | Playlist size as a display string (for example 34 videos). |
Queries that returned nothing carry success: false, search_query, a machine-readable code, a human-readable error, the service request_id, and the HTTP status_code. They are not charged.
Pricing
Pay-per-result. One charge per result delivered: $0.0002 each, which is $0.20 per 1,000 results. The charge fires only after a result row lands in your dataset.
- 1,000 delivered results cost $0.20.
- A run whose queries all return nothing costs $0.
- There is no per-run start fee, so an empty run is genuinely free.
Apify subscriber discounts apply automatically: roughly $0.16 per 1,000 on the Scale plan and $0.12 per 1,000 on Business. Platform compute is included in the per-result price. Cap the maximum spend of a single run from Apify's Run Limits panel; the Actor honors the cap and stops cleanly mid-walk when the budget runs out.
What this Actor does not do
Honest scope, so you know before you run it:
- YouTube sets the depth, not you. YouTube stops serving results for a query when relevance runs out, usually in the low hundreds. A high
maxResultsreturns everything available, which for many queries is fewer than the number you asked for. This is not a fixed cap like the Data API's 500, but it is not unlimited either. videois the default and returns only videos. Settypetochannelorplaylistfor those, orallfor a mix (which also surfaces Shorts). There is no standaloneshortstype; Shorts appear underall.- One row per result, with what search exposes. You get the title, channel, views, and thumbnail search shows. Full descriptions, like counts, comment counts, and tags require opening each video and are out of scope here.
- Public results only. The search returns what a signed-out visitor sees.
How to use this Actor
- Open the Actor in the Apify Console.
- Set
query(single),queries(a list), or both. Up to 50 unique queries run per job. - Set
maxResultsto cap results per query. Optionally settype(video, channel, playlist, or all),sortBy(relevance, date, views, rating),uploadDate, andduration. Filters apply to every query. - Click Start. Each result is one
success: truerecord. Queries that return nothing produce onesuccess: falserecord and do not stop the run. Only results are charged.
The Actor is also callable from the Apify API and every official integration (Make, Zapier, n8n, Slack, webhooks). The API tab in the Console has ready-to-paste JavaScript, Python, and curl snippets.
Reliability
A bad filter or a query with no results never fails the batch. An unusable filter value or a query that finds nothing becomes a success: false record with a specific code. The run keeps going and finishes successfully.
You never pay for a miss. Billing fires on an explicit per-result charge event, not on dataset writes, so success: false records are free. There is no per-run start fee either.
Hard 30-second deadline per page request. Each page of results is fetched under a 30-second deadline. If YouTube or the proxy network stalls, that query returns a 504 with code: DEADLINE_EXCEEDED as a success: false record instead of hanging your run.
FAQ
Do I need a YouTube Data API key?
No. The Actor reads YouTube's public search directly and does not consume Google API quota.
How many results can I get per query?
As many as YouTube serves for that term, capped by maxResults. YouTube stops paging when relevance runs out, which for most queries is in the low hundreds. There is no fixed 500-result ceiling like the official Data API, but no query is unlimited either.
Can I search for channels or playlists, not just videos?
Yes. Set type to channel or playlist. all returns a mix of videos, channels, playlists, and Shorts. The default, video, returns only videos.
Can I filter and sort?
Yes. sortBy takes relevance (default), date, views, or rating. uploadDate and duration narrow video results. Filters apply to every query in the run.
Why did I get fewer results than maxResults?
YouTube ran out of results for that query first. maxResults is a ceiling, not a guarantee; the depth of any search is set by YouTube, and the video filter is shallower than an unfiltered all search.
Can I also get transcripts, comments, or a channel's full uploads?
Yes, with the sibling Actors: YouTube Transcript Scraper for captions, YouTube Comments Scraper for comments, YouTube Channel Scraper for a channel's videos, and YouTube Shorts Scraper for Shorts. Search collects the video IDs; the sibling Actors go deep on each.
Can I use this Actor from my own code?
Yes. Use the Apify API or one of the official SDKs (Node.js: apify-client, Python: apify-client). The Console shows ready-to-paste code samples on the API tab.
How does billing know a query returned nothing?
Billing fires on an explicit per-result charge event, not on dataset writes. The Actor only fires it when a result is delivered, so success: false records sit in your dataset for free. You get every result in one place and still branch on success and code, with a request_id for support correlation.
Found a bug or want a feature?
Open an issue on this Actor's Issues tab and include the request_id from any error record you saw. We respond within one business day.