GitHub Actions can run a scheduled workflow that compares a YouTube playlist with a list you maintain in a repository, then adds or removes entries to match. The schedule is not an exact-time guarantee, playlist changes require OAuth, and deletion uses a playlist-item ID rather than the video ID.
This approach suits a rotation whose source of truth is a small, ordered configuration file: for example, a devotional channel that adds a new bhajan recording each week and removes older entries. It is less suitable when a change must happen at a precise minute or when you need a non-technical editor to manage the playlist without touching code.
What the workflow will automate
A scheduled workflow is a useful way to reconcile a playlist, not merely to run a sequence of blind edits. You store the playlist ID, the video IDs you want present, their intended order, and a retention policy in the repository. When GitHub starts the workflow, a script reads the current playlist, compares it with that desired state, and makes only the changes required.
For example, suppose a study channel keeps a playlist of the latest guided sessions. Its configuration can say which videos should remain, in what order, and whether the playlist should contain only those videos or retain a fixed number of recent additions. The workflow can add a missing video and remove an entry that no longer belongs. It can also report when the actual playlist differs from the intended one.
Keep the automation’s scope deliberately narrow. A workflow that manages one named playlist should not try to tidy unrelated playlists, infer which videos are old from their titles, or remove entries simply because one API response looked unexpected. Write down the intended policy before writing the script: does “rotation” mean a fixed curated sequence, a rolling set of recent uploads, or one addition and one removal per run? Those are different rules.
A playlist can contain entries that point to videos, but it is not itself a video library manager. The API’s playlist operations act on playlist entries. Your rotation script therefore needs to distinguish the identity of an entry from the identity of the video it references; that distinction matters most when removing something.
GitHub Actions gives you a trigger, a repository checkout, and a place to run the script. It does not make YouTube playlist edits possible by itself. The workflow needs a valid OAuth credential, network access to the YouTube Data API, and error handling that leaves enough information to understand a failed run.
If duplicate prevention is part of the policy, define it explicitly rather than assuming that each run is unique. The ideas in preventing duplicate episodes in a YouTube playlist also apply here: compare what is already present before inserting, and decide how to handle an already-listed video.
Prepare YouTube OAuth access
Playlist writes require OAuth authorization. An API key alone is not sufficient for inserting, updating, or deleting playlist entries. Google’s YouTube Data API authorisation overview describes the authorisation model, and the playlistItems.insert reference lists supported scopes for adding an entry.
Set up an OAuth client for the Google account that owns or can manage the channel and playlist. The consent and credential process is a Google-side setup task; do not assume that an unattended refresh token appears automatically just because you created a client. Test the chosen credential lifecycle with the account and playlist you intend to automate, and check Google’s current guidance before relying on it for unattended runs.
Use the narrowest supported scope that permits the changes you need. Google’s insert and delete references list scopes such as https://www.googleapis.com/auth/youtube and https://www.googleapis.com/auth/youtube.force-ssl. Check the current API documentation and your application’s requirements rather than requesting broad access without reason. A read-only scope cannot perform playlist writes.
Keep the OAuth client secret and refresh credential in GitHub Actions secrets, not in the repository, script, workflow log, or a committed local configuration file. Map them into only the step that needs them. A repository secret is straightforward to manage; an environment secret can be useful if you want protected deployment environments and reviewer approval before a workflow can use the credential. Choose based on who can change the workflow and the consequences of an unwanted playlist edit.
GitHub’s GITHUB_TOKEN is separate from the Google OAuth credential. It grants permissions to GitHub resources; it does not authorise a YouTube playlist change. Set repository-token permissions as narrowly as the workflow permits, following GitHub’s guidance on controlling GITHUB_TOKEN permissions. Avoid printing secrets, tokens, request headers, or encoded versions of credentials. Automatic log masking is not a reason to echo sensitive values.
Before enabling the schedule, use a test playlist or a non-destructive read-only pass to confirm that the OAuth account can see the intended playlist and its current entries. This helps separate an authentication problem from a script problem. It also gives you a place to verify the account’s permissions without experimenting on the live rotation.
Choose a schedule and timezone
A workflow file lives under .github/workflows/ and can use on.schedule with a POSIX cron expression. GitHub documents scheduled workflow syntax and supports a five-minute minimum interval. The schedule is interpreted in UTC unless you set an IANA timezone. The scheduled run uses the latest commit on the repository’s default branch, so changes to the cron expression or rotation configuration need to reach that branch before they affect normal scheduled runs. See GitHub’s workflow syntax reference.
Choose between machine time and local wall-clock time. If your policy is “run at the same UTC time every day”, leave the timezone unspecified and express the cron time in UTC. If a team in India wants a local morning run, an IANA timezone such as Asia/Kolkata can express that intent. A named timezone follows local clock rules where daylight saving time applies; GitHub documents that a scheduled time skipped during a spring-forward transition advances to the next valid time. India does not currently observe seasonal clock changes, but the timezone choice still makes the intent legible to anyone maintaining the workflow.
| Scheduling choice | What it means | Useful when | Trade-off |
|---|---|---|---|
| UTC cron, no timezone | The cron fields are read as UTC | You want a fixed machine-time cadence | The local run time may not be an intuitive working hour |
| IANA timezone | The cron follows the named local timezone | The schedule is defined by a local wall-clock routine | Daylight-saving transitions can affect the scheduled wall time in places that observe them |
| Less frequent runs | The workflow checks only when a rotation is due | Playlist changes are weekly or otherwise occasional | A missed run can leave the playlist out of date longer |
| Frequent runs | The workflow reconciles more often | You need quicker correction after a change | It still is not a punctual trigger, and unnecessary API calls add complexity |
Do not schedule a playlist rotation more often than its editorial policy requires. A devotional playlist that changes on Fridays does not need a five-minute reconciliation loop. Conversely, if you schedule a check every five minutes, that is a minimum interval GitHub documents, not a promise that each run begins precisely on the cron minute.
GitHub says scheduled events can be delayed during high load, particularly around the start of an hour, and queued runs can be dropped. Put the cron minute away from 00 where practical, but treat that only as a way to avoid a documented busy period, not as a timing guarantee. Add workflow_dispatch so an authorised maintainer can run the same workflow manually after a missed or failed scheduled execution.
There is an additional maintenance detail for public repositories: GitHub documents that scheduled workflows are automatically disabled after 60 days without repository activity. If your automation lives in a public repository that may remain untouched, account for that behaviour in monitoring and check GitHub’s current event documentation. The events that trigger workflows page is the source for the delay, dropped-run, and inactivity caveats.
Identify playlist items correctly
The YouTube Data API represents an entry in a playlist as a playlistItem. Each item has its own id; its snippet.resourceId.videoId identifies the video that entry points to. These IDs are not interchangeable. The video ID is useful for deciding whether a desired video is already represented. The playlist-item ID is what the delete operation requires.
When you list the playlist, retain both values for every result. Conceptually, a record might look like this:
{
"id": "PLAYLIST_ITEM_ID",
"snippet": {
"resourceId": {
"videoId": "VIDEO_ID"
}
}
}
The placeholders above are labels, not usable IDs. Your code should treat the outer id as the entry key and the nested videoId as the referenced content. Google’s playlistItems.list reference documents the playlist-item resource and fields to request.
This distinction prevents a particularly costly deletion mistake. If you decide that a particular video should be removed from a playlist, first find the matching playlist-item record, then pass that record’s id to playlistItems.delete. Do not pass the video ID to the delete method, and do not assume a video ID can be substituted. A video can also appear more than once in a playlist, so matching a video ID may find multiple entries. Your policy must say whether to remove one occurrence or all occurrences, and the script must select the corresponding playlist-item IDs.
A robust list step should account for pagination. Request enough fields to identify each entry and follow the API’s page token until you have read the whole playlist. Otherwise, a comparison against only the first page can misclassify an existing item as absent or overlook an unwanted entry. Keep the playlist ID and the returned item IDs together in the data structures used by the rest of the script, so that an ID from one playlist is not accidentally used in another.
Treat the API response as current evidence, not as a permanent map. If a human edits the playlist between your read and a destructive operation, the saved association may no longer reflect the state you intended to change. Re-list or otherwise confirm the current state immediately before removals, and make the script stop for review if the response does not match its assumptions.
Add and remove entries safely
The safest general pattern is “read, compare, reconcile”. Start with the desired ordered list from version-controlled configuration. Fetch the playlist’s current entries, build a set of existing video IDs for membership checks, and retain a mapping from each video ID to the playlist-item IDs that refer to it. Then calculate the smallest set of inserts, deletes, or position updates that brings the playlist towards the desired state.
For each missing video, call playlistItems.insert with the target snippet.playlistId and snippet.resourceId. For each unwanted occurrence, call playlistItems.delete with that exact playlist-item ID. If position matters, use playlistItems.update with the entry’s playlist-item ID and the intended position; do not assume that inserting entries alone will produce the order you want. The official playlistItems.delete reference specifies the item ID parameter for deletion.
Make the script idempotent: running it twice against an unchanged desired list should not create extra entries or remove additional ones. That means checking whether a video is already present before insertion, and checking the current entry again before deleting. If the API reports a duplicate, an inaccessible video, or an unsupported operation, stop or record a clear per-item error instead of silently pretending the rotation completed.
A read-and-diff workflow also avoids unnecessary writes. Google documents a quota cost of 1 unit for playlistItems.list, and 50 units for each playlistItems.insert or playlistItems.delete call in those API references. The quota cost is another reason not to delete and reinsert every entry on every run. Check the current project quota and API documentation rather than treating these per-call costs as the whole quota policy.
There are real limits to a reconciliation script. YouTube can reject a change if a playlist is inaccessible, an item cannot be added, the playlist is at its item limit, or the operation is unsupported for that playlist. Some special or system playlists do not support the same writes as an ordinary playlist. A successful OAuth exchange does not mean every target playlist operation will be accepted.
For a rolling rotation, decide what “oldest” means and how it is measured. Playlist position, upload date, and the time your configuration first included a video are different signals. If you keep the latest few entries by configuration order, document that policy and change the ordered configuration deliberately. Avoid inferring age from title text or deleting an entry merely because it was not present in a partial API result.
The operational side of the channel remains separate from playlist curation. If your goal is to keep a pre-recorded programme playing continuously, playlist automation alone does not make it a 24/7 live broadcast. The VLC video-source guide for an always-on store promo explains a different part of that setup; use it when the question is how to deliver a continuous live stream rather than how to maintain playlist entries.
Test and monitor the workflow
Test in layers. First, run the script locally or in a manual workflow against a test playlist, with only read access in the first pass if possible. Confirm that it can list every entry and prints a proposed diff without changing anything. Then exercise a controlled insert and delete on the test playlist, verifying that the deletion request used a playlist-item ID from the latest response.
Add a dry-run mode that calculates the proposed changes and reports them without calling write methods. Keep it as an easy way to inspect a configuration change before enabling it. For a live run, log the playlist identifier in a suitably limited form, the counts of entries read and changes attempted, and the result for each operation. Do not log OAuth secrets, refresh tokens, or full request headers.
Use the workflow_dispatch trigger as a recovery route and a testing path. A manual run should execute the same code and configuration as the scheduled run; avoid maintaining a separate “manual” script whose behaviour can drift. Restrict who can alter the workflow and its secrets, and consider requiring environment approval for a playlist whose contents have commercial or editorial significance.
A concise run summary is more useful than a green check alone. Report whether the workflow found the playlist, how many entries it compared, which changes succeeded, and whether any items needed attention. If a run fails after some writes have succeeded, the next run should re-read the playlist and continue from observed state. That is safer than assuming a failed job rolled back its earlier API calls.
Monitor for both workflow failures and missing expected runs. GitHub’s schedule is best-effort, so a green result from the previous week does not prove that today’s job started on time. For a weekly rotation, a simple check that the expected playlist state has been reached by a reasonable later point may be more useful than watching the cron minute. If an exact delivery window is business-critical, evaluate a scheduler with stronger delivery guarantees rather than treating GitHub Actions as a punctual service.
Keep the repository’s default branch and configuration in view. A schedule runs the latest commit on that branch, so a merge can change the next run’s desired state. Review changes to playlist IDs, ordered video lists, scopes, and deletion rules as operational changes, not merely code tidying. The match-replay playlist guide is another example where the ordering and retention policy should be explicit before automation is trusted.
The workflow should fail visibly when a required secret is absent, the API cannot authorise the account, or the playlist cannot be read. Avoid fallback behaviour that guesses a different playlist or quietly skips a failed deletion. For a small business promo loop, stale playlist contents can be more confusing than a clear alert that asks an owner to review the run.
When GitHub Actions is the wrong scheduler
GitHub Actions works well when the rotation is repository-managed, the schedule is approximate, and a maintainer is comfortable reviewing configuration changes. The code and desired playlist state stay together, and a manual run can provide a practical recovery path. It is also a reasonable fit for a weekly or daily content rotation where a delay is inconvenient but not consequential.
Choose another operating method when playlist changes must take place at an exact time, when non-technical staff need to adjust the sequence frequently, or when a missed run has material consequences. The platform’s documented delays and possibility of dropped queued jobs are a poor fit for a promise such as “this entry must be removed at exactly 09:00”. A scheduler can trigger the API operation, but it cannot eliminate the API’s authorisation and playlist constraints either.
Also separate playlist maintenance from always-on broadcast delivery. If your recurring task is to keep a fixed video running live while your own computer is switched off, automating a public playlist does not solve the streaming operation. StreamNeo removes the need to leave your computer running for that specific continuous-broadcast task: you upload the file, connect the channel, and the broadcast runs with monitoring and automatic restart if it drops. Playlist rotation remains a separate editorial job, and you should choose the tool according to which problem you are solving.
Before committing, compare the operating options on the pricing page. When the file and channel are ready, start free — 24-hour trial, no card.
FAQ
Can GitHub Actions run a playlist rotation at an exact time?
No. A cron expression defines when a scheduled event is eligible to run, but GitHub documents that scheduled events can be delayed under load and queued jobs may be dropped. Use monitoring and manual dispatch for recovery, and choose another scheduler if exact delivery timing is essential.
Can I delete a playlist entry with its video ID?
No. A playlist entry has its own playlist-item ID, while the video ID identifies the video referenced by that entry. Find the matching playlist item and use its id for deletion; a video may appear more than once, so your removal policy should handle each occurrence deliberately.
Is a YouTube API key enough to edit a playlist?
No. Playlist writes require OAuth authorisation with a supported YouTube scope. An API key does not substitute for the channel owner’s authorised credential, and you should keep OAuth credentials in GitHub secrets rather than in code or logs.
Should I use UTC or a local timezone?
Use UTC when the rotation is defined by a fixed machine-time cadence. Use an IANA timezone when the schedule should follow a local wall-clock time, and account for daylight-saving transitions where they apply. In either case, the scheduled run is not guaranteed to begin at the exact cron minute.