# Changelog of YouTube Search Scraper — Videos by Keyword, Exact Stats (`foxlabs/youtube-search-scraper`) Actor

- **URL**: https://apify.com/foxlabs/youtube-search-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/foxlabs/youtube-search-scraper.md

## Changelog

### 2026-10-08 — builds 0.1.11 to 0.1.13

Changes since the build that was published on 2026-10-07 (0.1.10). No pricing change.

- **A Short whose owner has turned embedding off now comes with its channel's @handle** (`channelHandle`), read from the Short's own page. It used to have the channel's ID and name and no @handle. One Short in 200 was such a Short in our measurement.
- A run of an "only new" list that is killed by its timeout was tested on this Actor on the platform: a run killed after 147 videos, 37 of them not yet in its list, was followed by a run that read them back and delivered 443 other videos and none of the 147 again. The README said such a run "can deliver its last videos — up to nine — again"; that sentence is gone.
- README rebuilt in the foXLabs layout: use with AI agents (Apify MCP server, agentic payments), getting started, use cases, integration, a price table, legal notes. The measured figures are the ones of 2026-10-07.
- **Wording of a run's last word (0.1.13).** A run that delivers nothing used to say "nothing charged". The run-start event ($0.00005) is charged for every run, so the message now says "no rows charged". Nothing about charging itself changed.
- **README corrected after an independent fact-check (0.1.13):** 26 statements were wrong or said more than the measurements do, 7 had no record behind them. Among them: the prefilled input takes 3 to 7 seconds of run time, not "4 to 5"; a video less than a day old whose hour holds midnight UTC has no `publishedDate` at any level; YouTube refused video pages in 4 of 11 runs that read them, and once in a Shorts run; a restarted test run had 152 rows, not 129, when it was restarted; "only new" delivers what is new to the list among each keyword's first results, not every new upload; `isMembersOnly` has never been seen in a search result and is no longer listed as a feature. The full list is in the Actor's QA record.
- Build 0.1.12 only takes in the series' shared code as it stands (a helper this Actor does not call); nothing changes in what it delivers.
- The record of an "only new" list no longer carries the mark that said whether the list's last run ended without being cut short; it never changed what this Actor delivers.

### 0.1 — 2026-10-07

First version.

- YouTube search for any number of keywords (up to 1,000 a run): videos, Shorts, or both, in the order YouTube returns them (`searchRank`). A video two keywords find is delivered once, under the keyword that reached it first.
- A search is followed to its end and — because YouTube returns a different part of a search every time it is asked — again, up to four more times, while that brings enough new videos. `searchPass` says which of YouTube's answers a result stood in (1 = the first).
- Three levels of detail per video (`details`):
  - `figures` (the default) — the publish date and the like count, on top of the search result (one small request per video);
  - `full` — the video's own page: the same, plus the number of comments, the full description, hashtags and the page's chapters — the creator's, or the ones YouTube generates itself where the creator set none (`chaptersAutoGenerated`) — one large request per video; slower, and limited by YouTube;
  - `list` — the search result alone: title, channel, unrounded view count, duration, "7 months ago".
- The full description (`details: "full"`) puts back the links YouTube shows shortened as their full addresses; an address the creator pasted already cut stays as written.
- What a search result itself carries is delivered at every level, where YouTube shows it: chapters, YouTube's AI-generated summary, the other channels of a collaboration, the number of tagged products, the planned start of a premiere or stream.
- The channel behind each result (`channelStats`, on by default): subscribers, country, lifetime views, number of videos — one request per channel of the run — and `viewsPerSubscriber` for the video. Filter on it with `minViewsPerSubscriber`.
- YouTube's own filters: upload date (today, week, month, year), duration, features (live, 4K, HD, subtitles, Creative Commons, 360°, VR180, 3D, HDR, location), and the Popularity order. The answer says which filters YouTube applied; one it did not apply is named in the status message.
- An upload-date filter delivers only what matches it: older videos YouTube adds when it runs out of matching ones are left out, and counted in the status message.
- `exactSpelling`: YouTube searches for its own spelling of what it takes for a typo; the status message says when it did, and this option searches the keyword as typed.
- Shorts: YouTube answers a Shorts search in three shapes (shelf cards, grid items, plain video results), all read. A result that names no channel gets it from YouTube's public embed answer and its @handle. An empty answer that comes with an estimate of results is asked for three times before the search is called empty.
- Streams on air (`isLive`, `liveViewers`), premieres and streams that have not started (`isUpcoming`, `scheduledStartAt`) and finished streams (`wasLive`) are told apart.
- Like counts: a video nobody has liked yet has `likes: 0`; a channel that hides its like counts gives `null`. Comment counts (`details: "full"`): a video nobody has commented on has `commentsCount: 0`; `null` with `commentsOff: true` when comments are turned off.
- A Short whose owner has turned embedding off gets its channel from its own page; an @handle that YouTube answers with another address of the same channel is followed to the channel.
- When a video's page cannot be read, the video is not left out: it comes with its exact figures (`detailLevel: "figures"`), and the status message says how many.
- "Only new videos": a named list remembers delivered videos; later runs look at the top of each keyword's results and deliver only what they have not delivered before. "New" is new to the list, not newly uploaded — YouTube shows a different part of a search each time; for new uploads only, run daily with `uploadedWithin: "today"`.
- Pay per delivered row, by its level of detail: `video-exact`, `video-detailed` or `video`. Status rows are free.
- A keyword without results gets a free status row that says so; a search YouTube answers with an error is reported as a failure, not as "no results".
- A run the platform restarts continues from the rows it already delivered.
- No proxy to set up: requests go out directly; a kind of request YouTube refuses (in our runs only video pages) is moved to Apify Proxy by the Actor itself, the others stay direct.
- `minViewsPerSubscriber` needs a subscriber count: with `channelStats` off and without `details: "full"` the input is refused. The duration filter is for videos: a Shorts search is sent without it.
- A run whose filters removed every video writes one free status row with the run's last word, so its dataset does not come back empty without a reason.
- Standby mode (`/search`) in the same code; this build is published as a batch Actor.
- "Only new" lists and restarts: the rows a run wrote before the platform restarted it are taken into the list when the run continues, so the list's next run does not deliver them a second time.
- A write of the list that fails no longer ends the run: it is made up for with the next rows and at the end of the run, and if the list still cannot be saved the status message says so. A list that has reached its size says in the status message how many of its oldest entries it let go.
- `listName` takes letters, digits and hyphens (a hyphen not first or last; capitals are read as lower case). A name with "\_" or a hyphen at its end used to pass the input check and then fail the run at Apify's storage; it is now refused before the run starts.
- A run the platform restarts does not write a status row a second time for a search or line it had already found empty.
- `publishedDate` of a video less than a day old is given only when the hour YouTube names ("7 hours ago" is 7 to 8 hours) lies within one UTC day; when that hour holds midnight the field is `null` instead of the later of the two days. `daysSincePublished` is 0 for such a video.
- Ages read from "1 month ago" / "2 years ago" get bounds that hold whether YouTube counts 30-day or calendar months (28 to 61 days, 730 to 1,097): a video is ruled out by its list text only when it is certainly too old.
- A run of an "only new" list that is killed (a timeout, the memory limit) cannot save the last rows it delivered. The list's next run now reads them back from that run's dataset before it starts, so they are not delivered a second time.
