Automating a YouTube Video Showcase in a Profile README

June 9, 2026 (1mo ago) · 4 min read

Originally written as internal engineering notes while building the thing it describes. Republished here with light editing — the timeframe is noted where it matters.

A GitHub profile README that showcases your latest videos has an obvious failure mode: the moment you publish a new one, the README is out of date, and hand-editing it every time is the kind of chore nobody keeps up for long.

The fix is to stop maintaining that section by hand. A scheduled GitHub Actions workflow can fetch the channel's latest uploads, render them as SVG cards, and commit the result back into the README — so the showcase updates itself whether or not I remember it exists.

This is how I set it up, using DenverCoder1/github-readme-youtube-cards, plus the parts of the setup that are easy to get wrong.

How it fits together#

Three pieces have to exist before anything runs:

  1. A workflow file at .github/workflows/youtube-cards.yml.
  2. A video showcase section in README.md.
  3. A pair of HTML comment markers around that section:
<!-- BEGIN YOUTUBE-CARDS -->
<!-- END YOUTUBE-CARDS -->

The markers are the load-bearing part. The action does not parse your README or guess where the cards belong — it replaces everything between those two comments and leaves the rest of the file alone. Delete or rename one of them and the workflow will run happily and change nothing.

Finding the channel ID#

The workflow is keyed on a channel ID, not a handle. There are three reliable ways to get it:

From the channel URL. If it looks like https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxxxxxxxx, the UC… segment is the ID.

From YouTube Studio. Avatar → YouTube StudioSettingsChannelAdvanced settings. The ID is in the "Channel ID" field.

From any video. Right-click the channel name under a video, copy the link address, and take the UC… part.

Then set it in the workflow:

channel_id: UCipSxT7a3rn81vGLw9lqRkg   # replace with your own

If the README also carries a subscribe button, that one does use the handle rather than the ID — the @name from your channel page URL:

<a href="https://www.youtube.com/@YourChannelHandle?sub_confirmation=1">

The first run#

Scheduled workflows don't fire on commit, so after pushing the workflow and README changes, the first run has to be triggered by hand: repository → ActionsGitHub Readme YouTube CardsRun workflow → pick the branch.

A few seconds later it fetches the latest videos, generates the SVG cards, rewrites the block between the markers, and commits the change. After that, the schedule takes over.

The options worth changing#

The defaults are sensible, but four settings are worth a look:

max_videos: 6          # how many cards to render
card_width: 250        # px per card
max_title_lines: 2     # truncation for long video titles
schedule:
  - cron: "0 */6 * * *"   # every 6 hours
  # - cron: "0 0 * * *"   # once a day

Card width and video count interact with how the cards wrap in the README, so it is worth iterating on those two together rather than separately.

On the cron: a frequent schedule costs nothing but generates a commit each time the video list actually changes. Every six hours is a reasonable ceiling for a channel that publishes weekly; daily is plenty for most.

Optional: showing video duration#

Duration isn't in the free feed — it requires the YouTube Data API. If you want it:

  1. In the Google Cloud Console, create or pick a project, enable YouTube Data API v3, and create an API key.
  2. Add it to the repository under SettingsSecrets and variablesActionsNew repository secret, named YOUTUBE_API_KEY.
  3. Reference it from the workflow:
youtube_api_key: ${{ secrets.YOUTUBE_API_KEY }}
show_duration: true

Never inline the key in the workflow file — the workflow is public if the repository is.

The two things that actually break#

Write permission. The workflow commits to your repository, so it needs contents: write. Without it the run fails at the very last step, after everything else has worked, which makes it look like a rendering problem rather than a permissions one.

Branch protection. If the default branch is protected, the Actions bot may be blocked from pushing. The workflow succeeds at generating the cards and then cannot land them.

Beyond those: if the run fails, check the channel ID first and read the Actions log — it is specific about what it could not fetch. If the run succeeds but the README doesn't change, the cause is almost always the marker comments (missing, renamed, or the README path misconfigured) rather than the action.

Why bother#

The value here isn't the video cards. It's that a section of a hand-maintained document has been converted into a derived artefact — something regenerated from a source of truth on a schedule, which cannot drift because nothing is asking a human to remember it. That pattern is worth applying to more of a profile README than just this.

Reference#