GuidesFEEDS

YouTube RSS feed URL for any channel or playlist

Every YouTube channel has a public Atom feed at https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID that lists the channel's 15 most recent uploads with no API key and no account needed. Playlists use the same URL with playlist_id=PLAYLIST_ID. YouTube sends no Access-Control-Allow-Origin header on the feed, so browser JavaScript reads it through https://proxy.cors.dev/, which is free and needs no signup.

Feed URL formats

The base URL https://www.youtube.com/feeds/videos.xml takes one query parameter: channel_id, playlist_id or the older user. Channel IDs start with UC. A channel's uploads are also available as playlists: replace the UC at the start of the channel ID with UU for all uploads, UULF for uploads without Shorts, or UUSH for Shorts only. The table below uses the Google for Developers channel, UC_x5XG1OV2P6uZZ5FSM9Ttw.

YouTube feed URLs for the Google for Developers channel
FeedURLEntries
Channel uploadshttps://www.youtube.com/feeds/videos.xml?channel_id=UC_x5XG1OV2P6uZZ5FSM9Ttw15 latest, Shorts included
All uploads (UU)https://www.youtube.com/feeds/videos.xml?playlist_id=UU_x5XG1OV2P6uZZ5FSM9Ttw15 latest, Shorts included
Uploads without Shorts (UULF)https://www.youtube.com/feeds/videos.xml?playlist_id=UULF_x5XG1OV2P6uZZ5FSM9Ttw15 latest, no Shorts
Shorts only (UUSH)https://www.youtube.com/feeds/videos.xml?playlist_id=UUSH_x5XG1OV2P6uZZ5FSM9Ttw15 latest Shorts
Playlisthttps://www.youtube.com/feeds/videos.xml?playlist_id=PLOU2XLYxmsILZpGc5gWRO1vL0n9CtcPJLUp to 15
Legacy usernamehttps://www.youtube.com/feeds/videos.xml?user=GoogleDevelopers15 latest, channels with a username
@handlehttps://www.youtube.com/feeds/videos.xml?handle=@GoogleDevelopersNot supported: HTTP 400

The feed does not accept an @handle: ?handle= returns HTTP 400. Use the channel ID instead; the next section shows where to find it.

Find the channel ID

  1. Open the channel page

    Open the channel on YouTube, for example https://www.youtube.com/@GoogleDevelopers.

  2. Open the About panel

    Click the "...more" link after the channel description to open the About panel.

  3. Copy the channel ID

    Scroll to "Share channel" and choose "Copy channel ID". The ID starts with UC; paste it after channel_id= in the feed URL.

Another way to find the ID is to view the channel page's source and search for channel_id=. The page contains a <link rel="alternate" type="application/rss+xml"> tag with the full feed URL. Your code cannot read the channel page through the free proxy: in our test the page HTML was about 1.6 MB, above the 1 MiB response cap, and the proxy answered 502 with X-Cors-Error: response_too_large. Look the ID up once and keep it in your code or config.

Fetch the feed in JavaScript

A direct fetch() of the feed from a web page fails because YouTube's feed response has no Access-Control-Allow-Origin header. Chrome logs No 'Access-Control-Allow-Origin' header is present on the requested resource and fetch() rejects with TypeError: Failed to fetch. Prefix the feed URL with https://proxy.cors.dev/: the proxy fetches the feed and returns it with Access-Control-Allow-Origin set to your page's origin. The free tier needs no signup and no API key, and accepts GET requests with XML responses up to 1 MiB, while the feed is about 25 KB.

Fetch a channel feed and read the videos
const channelId = 'UC_x5XG1OV2P6uZZ5FSM9Ttw';
const feedUrl = `https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`;

const response = await fetch(`https://proxy.cors.dev/${feedUrl}`, { credentials: 'omit' });
if (!response.ok) {
  throw new Error(`Feed request failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);
}

const feed = new DOMParser().parseFromString(await response.text(), 'application/xml');
if (feed.querySelector('parsererror')) throw new Error('The feed is not valid XML');

const YT = 'http://www.youtube.com/xml/schemas/2015';
const MEDIA = 'http://search.yahoo.com/mrss/';

const videos = [...feed.getElementsByTagName('entry')].map((entry) => ({
  id: entry.getElementsByTagNameNS(YT, 'videoId')[0].textContent,
  title: entry.getElementsByTagName('title')[0].textContent,
  url: entry.querySelector('link[rel="alternate"]').getAttribute('href'),
  published: new Date(entry.getElementsByTagName('published')[0].textContent),
  thumbnail: entry.getElementsByTagNameNS(MEDIA, 'thumbnail')[0].getAttribute('url'),
  views: Number(entry.getElementsByTagNameNS(MEDIA, 'statistics')[0]?.getAttribute('views') ?? 0),
}));

console.log(videos.length, videos[0]);

The code parses the XML with DOMParser and turns each entry into an object with the video ID, title, watch URL, publish date, thumbnail URL and view count. yt:videoId, media:thumbnail and media:statistics use XML namespaces, so the code reads them with getElementsByTagNameNS(). When a request fails, the proxy puts an error code in the X-Cors-Error response header, and the code adds it to the error message.

Show the latest videos on your page
// Markup: <ul id="videos"></ul>
const list = document.querySelector('#videos');

list.replaceChildren(
  ...videos.slice(0, 6).map((video) => {
    const item = document.createElement('li');
    const link = document.createElement('a');
    const thumbnail = document.createElement('img');

    link.href = video.url;
    thumbnail.src = video.thumbnail;
    thumbnail.alt = '';
    thumbnail.width = 480;
    thumbnail.height = 360;
    thumbnail.loading = 'lazy';

    link.append(thumbnail, video.title);
    item.append(link);
    return item;
  }),
);

The render code creates elements with DOM methods and adds titles as text, so a video title cannot inject HTML. Each thumbnail is the hqdefault.jpg image at 480 by 360 pixels. The code sets width and height to reserve the space and loads images lazily.

Fields in each entry

Each entry element holds one video. The feed itself also carries the channel name in title and a link to the channel page. The table lists the entry fields most pages need.

Fields in each YouTube feed entry
ElementNamespaceValue
yt:videoIdhttp://www.youtube.com/xml/schemas/2015Video ID
titleAtomVideo title
link rel="alternate"AtomWatch URL, or a /shorts/ URL for Shorts
publishedAtomUpload time with UTC offset
media:thumbnailhttp://search.yahoo.com/mrss/hqdefault.jpg URL, 480 by 360
media:descriptionhttp://search.yahoo.com/mrss/Video description
media:statisticshttp://search.yahoo.com/mrss/View count in the views attribute

Limits

  • The feed returns only the 15 most recent entries and has no paging; older videos need the YouTube Data API, which requires an API key.
  • YouTube serves the feed with Cache-Control: public, max-age=900, which lets caches reuse a copy for 15 minutes, so polling more often than every 15 minutes gains little.
  • The proxy does not cache feeds (Cache-Control: no-store) and applies fair-use rate limits per IP; each visitor's browser sends its own requests, so the limit applies per visitor.
  • Shorts appear in the channel feed with links like https://www.youtube.com/shorts/VIDEO_ID; use the UULF playlist to leave them out.

Good questions.

What is the RSS feed URL for a YouTube channel?

The URL is https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID. The channel ID starts with UC. The feed is Atom XML with the 15 latest uploads.

How do I get a YouTube RSS feed without Shorts?

Use playlist_id= with the channel ID's UC prefix replaced by UULF. UUSH gives Shorts only, while the plain channel feed includes Shorts.

Does the YouTube RSS feed need an API key?

No, the feed is public and needs no API key or account. The YouTube Data API needs a key, and it is the option for more than the latest 15 videos.

How many videos does a YouTube RSS feed show?

Channel feeds show the 15 most recent uploads, and playlist feeds show up to 15 entries. There is no paging parameter.

Why does fetch() fail when I load a YouTube RSS feed?

YouTube does not send Access-Control-Allow-Origin for the feed, so the browser blocks your page from reading it. Server-side code is not affected. In browser code, prefix the feed URL with https://proxy.cors.dev/.

Can I use a channel's @handle in the feed URL?

No, ?handle= returns HTTP 400. The feed needs the channel ID from the channel's About panel ("Share channel", then "Copy channel ID").