# Changelog of YouTube Channel Finder — Keyword & Similar Channels (`foxlabs/youtube-channel-finder`) Actor

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

## Changelog

### 2026-10-08 — build 0.1.21

No field of the input form changed, no output column was added or removed, no pricing change. What changes concerns channels whose recent videos YouTube gave through neither their feed nor their uploads list — as rows, as candidates of a seed and as seeds — and when a run that delivers nothing ends as failed.

- **A channel whose recent videos could not be read is left for a later run.** Such a channel used to be delivered as a keyword row when neither `minKeywordMatchPercent` nor a filter on recent videos was set — with `recentVideosAnalyzed: null`, empty upload and view columns and no topic — and charged like any other row. A row like that holds neither the figures nor the topic a channel is paid for. Now the channel is not delivered, not charged, and a list (`onlyNewChannels`) does not remember it, so the list's next run can deliver it whole. (A candidate of a seed was never delivered in that state: without titles it cannot be scored.) An uploads list that holds nothing but a stream on air or a premiere to come gives no upload to read either; we have seen no such list. In our runs of 2026-10-08 the uploads list answered every time it was asked (five platform runs of build 0.1.20: 49, 129, 191, 91 and 419 lists), so no such channel was seen. The rule is checked by this Actor's tests; the Channel Email Scraper, which reads a channel's recent videos with the same code, ran it on the platform with the test switch below (its build 0.1.26).
- **A seed whose videos cannot be read is reported as such.** A seed is profiled from its Videos tab and its feed. When the feed did not come and the Videos tab gave nothing — a channel that uploads Shorts only has none — the seed's line used to end with "YouTube listed no other channels next to its videos or for its topics". Now the seed's uploads list is read for its titles, and when that gives none either the line gets the status row "…: its videos could not be read (YouTube's feed did not answer, and neither its Videos tab nor its uploads list gave them); run it again later." This is checked by tests on recorded and made-up answers only: we have no seed on record whose Videos tab is empty.
- **When a run that delivers nothing ends as failed.** It did when more channels had been left for want of their recent videos than all filters together had turned down — so a search that brought many small channels could end as a normal run that advised loosening the filters while YouTube's feed was the reason. Now what the subscriber and country filters turned down is left out of the comparison: the run fails when, of the channels that passed those two, more were left than were turned down.
- **The status message.** Channels YouTube did not give enough of are no filter's doing: in the run's status message they are no longer listed among "did not pass your filters" but counted apart — "N channels were left for a later run: YouTube's feed did not answer, and the uploads list did not give what is needed — their recent videos, or the figure a filter asks for. They are not charged, and a list does not remember them". A run that delivers nothing for that reason and fails says "Nothing delivered and no rows charged: YouTube's feed did not answer, and the uploads list did not give what is needed either."; one that does not fail ends with "Run it again later for the channels whose feed did not answer." A seed's status row counts its candidates in that state as "N whose recent videos could not be read: YouTube's feed did not answer, and the uploads list did not give them" (it said "N recent videos not known (YouTube's feed did not answer)") — candidates that could not be scored, which is not the number of channels a later run would deliver. A run that stops at its reading limit while channels were left says "too few of them could be delivered" where it said "the filters let too few through". The run's own report (`SOURCE_REPORT`) keeps the key "recent videos not known (YouTube's feed did not answer)".
- **A test switch, for our own checks.** What a run does while YouTube's feed fails could be checked on the platform only during a failure. A build that does not carry the tag `latest` now reads one more input value, `debugFail` (`feed`, `uploads` or both), that makes it treat the feed or the uploads list as unanswered without asking YouTube; such a run says "TEST RUN" in its log. A build tagged `latest` ignores it — and a build that loses that tag to a newer one honours it again when it is run by its number.
- The fault of 2026-10-08 ended at some time between 06:47 UTC, when our last run with failing feeds ended, and 09:48, when all 40 feeds we asked for answered at the first request.

### 2026-10-08 — build 0.1.20

One output column was added: `recentVideosSource`. No input field changed, no column was removed, no pricing change. The values that change are those of a channel that has videos and whose feed YouTube did not give: its upload and view columns, `null` since 0.1.17, are now filled from the channel's uploads list, and its topic is read from that list's titles (since 0.1.17 it came from the Videos tab).

- **Without the feed, topic and figures come from the channel's uploads list.** YouTube's feed service fails for hours at a time: on 2026-10-08 it had begun by 01:25 UTC, and at 04:56 none of 40 feeds came in six tries each — a request from outside Apify got "not found" as well, and others have reported the same since December 2025. A channel's uploads list (its page "Uploads from …", one request) answered whenever we asked that morning (locally, through a proxy): for all 40 of those channels at 05:19, and in two local test runs of this Actor, where it was asked for 54 and 185 channels, none had to be turned down for lack of it. It lists the channel's latest uploads of every kind, as the feed does, with YouTube's rounded views ("9.8M views") and their ages in words ("3 weeks ago") — and without likes or exact times. So, for a channel whose feed does not come (after six requests, or two while the service is down for nearly every channel):
  - `topKeywords`, `keywordMatch` and the similarity to a seed are read from the titles of the list's 15 newest uploads. For 35 of the 40 channels those were exactly the uploads of the feed recorded the day before; the Videos tab, read for this since 0.1.17, lists long videos only;
  - `recentVideosAnalyzed`, `avgViewsRecent`, `medianViewsRecent`, `viewsPerSubscriber`, `daysSinceLastUpload`, `uploadsLast30Days`, `uploadsPerMonth`, `recentVideosSpanDays`, `latestVideo` and `topRecentVideo` are read from that list, and the new column `recentVideosSource` says `uploads list` (`feed` in every other row that has recent videos);
  - against the feeds of the day before: median views within ±10% for 38 of the 40 channels; for the 33 channels with the same 15 uploads in both and ages in the list, uploads in the last 30 days the same for all 33, days since the last upload the same for the 10 that had uploaded within two weeks and within ±15 days for 24 of the 33; uploads per month within ±25% for 24 of the 31 for which both sources give one;
  - `lastUploadAt`, `avgLikesRecent`, `likeRate` and `shortsShareRecent` stay `null`: the list does not carry what they need;
  - a channel that uploads Shorts only shows its list without ages (2 of the 40): its topic and view columns are filled, its activity columns stay `null`;
  - `uploadsPerMonth` and `recentVideosSpanDays` are `null` when the newest and the oldest of the uploads are worded alike in weeks, months or years ("2 years ago"): no span is made up for them;
  - `uploadedWithinDays` and `minMedianViews` judge the channel by these figures; only a figure the list does not carry is turned down as "recent videos not known (YouTube's feed did not answer)";
  - when the list does not answer either, the row is as before (`recentVideosAnalyzed: null`, and `recentVideosSource: null`), its topic rests on the description alone, and a topic or similarity threshold turns it down as "recent videos not known";
  - the status message counts both kinds of rows, and they are charged like any other row.
- A seed channel is profiled as before, from its Videos tab and — when it answers — its feed; with the feed down a seed with about 200 candidates cost about 800 requests and 77 seconds in a local test run (about 400 requests with the feeds up).

### 2026-10-08 — builds 0.1.15 to 0.1.19

Changes since the build that was published on 2026-10-07 (0.1.14). No input field or output column was added or removed, no pricing change. One value changed for one case — a channel that has videos and whose feed YouTube did not give: `recentVideosAnalyzed` is now `null` (it was `0`).

- **When YouTube's feed fails, the Actor asks again, reads the topic elsewhere and says what it could not read (0.1.17, 0.1.18).** The recent-video figures and the titles a channel's topic is read from come from its public feed. On 2026-10-08 — by our run records from about 01:25 UTC, and still at our last reading at 03:21 — YouTube's feed service answered "not found" or "server error" for channels that have a feed: of 40 feeds that had answered the day before, 8 answered a single request at 02:01 UTC and 14 at 02:25. Until this build a channel in that state was delivered as if it had no uploads (`recentVideosAnalyzed: 0`, no `topKeywords` from titles, `keywordMatch: null`); a filter on recent videos turned it down as "no recent upload", `minKeywordMatchPercent` turned it down as off topic, and its similarity to a seed was worked out from its description alone. Now:
  - a feed that does not come is asked up to five more times (that brought 25 of the 40 at 02:01 and 39 at 02:25);
  - (0.1.18) while the last channels of a run bring next to no feeds, a feed is asked one more time instead of five: at 02:58 UTC the service was down almost altogether — the same forty channels gave 2 feeds in six tries each, and twenty of them, asked from eight addresses, 13 feeds in 312 answers — and asking five more times only made a run slower;
  - a channel whose feed still does not come has its topic read from the titles on its Videos tab (one more request), so `topKeywords`, `keywordMatch` and the similarity to a seed are still there;
  - its row carries `recentVideosAnalyzed: null` and empty upload, view and like columns — not known, instead of none — and the status message counts such rows;
  - `uploadedWithinDays` and `minMedianViews` turn such a channel down as "recent videos not known (YouTube's feed did not answer)", and so does a topic or similarity threshold when neither the feed nor the Videos tab gave titles; when that is most of what was turned down and nothing could be delivered, the run fails with that reason instead of advising you to loosen your filters;
  - a channel without public videos is unchanged: `recentVideosAnalyzed: 0`;
  - a row without its recent-video figures is charged like any other row, and an "only new" list remembers it like any other.

- **Wording of a run's last word (0.1.19).** 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.19):** 11 statements were wrong or said more than the measurements do, 7 had no record behind them. Among them: one keyword followed to the end brought 602 to 815 channels (not "664 to 815"); only regular plurals and a list of irregular ones count as their singular; YouTube did refuse requests in 3 of 29 similar-channel runs; the outage of YouTube's feed service began around 01:25 UTC, not after 01:30; a row without figures is charged, a later run for its figures is charged again, and a list does not deliver the channel twice. The full list is in the Actor's QA record.

- **A seed and a similar channel are named even when their feed gives nothing.** YouTube's feed answers 404 for some channels. A seed in that state came with `seedTitle: null` on its rows, and a similar channel found next to the seed's videos with `title: null` (1 row of 24 and 1 of 40 in two runs; the golden set caught the seed on 2026-10-08). The channel's own page names it now.

- **A run that delivers nothing says why in its dataset too.** When every channel was filtered out, a run used to end with an empty dataset and the reason in its status message only. It now also writes one free status row with that message, as the comments and search Actors of this series do, so a call that reads only the dataset is not left with an empty answer. An "only new" run that finds nothing new still leaves the dataset empty.

- 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.

- A run of an "only new" list that is killed by its timeout was tested on this Actor on the platform: the list's next run read the killed run's rows back and delivered none of them again.

- 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.

- Channels by keyword: YouTube's channel search and the creators behind the videos that rank for the keyword, one row per channel.
- A search is followed to the end of YouTube's results and then again, up to five times, while a pass still brings enough channels the earlier ones did not.
- Optional widening: the phrases YouTube's search box suggests for a keyword are searched too; every row names the search that found it.
- Similar channels for a seed channel: candidates from the "related videos" next to the seed's latest videos and from searches for the seed's topics; each candidate is scored against the seed and delivered best first, with the shared topics, the similarity and the number of seed videos it was shown next to.
- Per channel: subscribers, size tier, total views, country, verified badge, median and average views of the latest uploads (15 at most), like rate, uploads per month, days since the last upload, share of Shorts, latest and best recent video, and the terms that recur in its video titles.
- `keywordMatch`: the share of a channel's recent video titles that mention the keyword's topic.
- Filters: subscriber range, countries, recent upload, median views, minimum similarity. Channels that do not pass are not delivered and not charged.
- "Only new channels": a named list remembers delivered channels and later runs skip them.
- Pay per delivered row: `similar-channel` for a row of a "similar to" search, `channel` for a row found by keyword. Status rows are free.
- A keyword or seed that brings nothing gets a free status row that says why; a run that stops at its limit says which searches it did not get to.
- A run the platform restarts continues from the rows it already delivered; a seed that was delivered in full is not scored again.
- No proxy to set up: requests go out directly; a kind of request YouTube refuses — or keeps leaving unanswered — is moved to Apify Proxy by the Actor itself, the others stay direct.
- Seed channel lines: an @handle in any script, a channel's old custom address (youtube.com/name), and an @handle that YouTube answers with another address of the same channel all lead to the channel.
- Rows are written in the order they were delivered, also when a run mixes the two events.
- "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.
- 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.
