Skip to Content
DevelopmentAPIParsing Feeds

Parsing Feeds

Comprehensive guide to parsing RSS/Atom feeds with rss.Today.

Supported Formats

The Parser class automatically detects and parses:

  • RSS 0.9 - Original RSS format
  • RSS 1.0 - RDF-based RSS format
  • RSS 2.0 - Most common RSS format
  • Atom 1.0 - Atom syndication format
  • JSON Feed 1.0 - JSON-based feed format

Input Sources

Parse from URL

Fetch and parse a feed from a URL:

import { Parser } from 'rss.today'; const parser = new Parser(); const feed = await parser.parseURL('https://example.com/feed.xml');

Custom headers:

const parser = new Parser({ headers: { 'User-Agent': 'MyApp/1.0', 'Authorization': 'Bearer token' } }); const feed = await parser.parseURL('https://example.com/feed.xml');

Parse from String

Parse XML from a string:

const xml = `<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <channel> <title>Example Feed</title> <link>https://example.com</link> <description>An example feed</description> <item> <title>First Item</title> <link>https://example.com/first</link> </item> </channel> </rss>`; const feed = await parser.parseString(xml);

Parse from File

Parse a local file:

const feed = await parser.parseFile('./feeds/my-feed.xml');

Parse from Buffer

Parse from a Buffer:

const buffer = Buffer.from(xmlContent, 'utf-8'); const feed = await parser.parseBuffer(buffer); // With custom encoding const buffer = Buffer.from(xmlContent, 'utf-16'); const feed = await parser.parseBuffer(buffer, 'utf-16');

Parse from Stream

Parse from a Node.js Readable stream:

import * as fs from 'fs'; const stream = fs.createReadStream('./feed.xml'); const feed = await parser.parseStream(stream); // Custom chunk size limit const feed = await parser.parseStream(stream, 5 * 1024 * 1024); // 5MB

Parse JSON Feed

Parse JSON Feed 1.0 format:

const jsonFeed = `{ "version": "https://jsonfeed.org/version/1", "title": "Example Feed", "home_page_url": "https://example.com", "feed_url": "https://example.com/feed.json", "items": [ { "id": "1", "title": "First Item", "url": "https://example.com/first", "date_published": "2024-01-01T00:00:00Z" } ] }`; const feed = await parser.parseJSON(jsonFeed);

Parsed Feed Structure

ParsedFeed Interface

interface ParsedFeed { title?: string; // Feed title description?: string; // Feed description link?: string; // Website URL feedUrl?: string; // Feed URL language?: string; // Language code lastBuildDate?: string; // Last build date image?: { // Feed image url?: string; title?: string; link?: string; width?: string; height?: string; }; items: ParsedItem[]; // Feed items paginationLinks?: Record<string, string>; // Pagination links itunes?: any; // Podcast metadata (iTunes) [key: string]: any; // Custom fields }

ParsedItem Interface

interface ParsedItem { title?: string; // Item title link?: string; // Item URL pubDate?: string; // Publication date date?: string; // Date (alternative field) isoDate?: string; // ISO 8601 formatted date author?: string; // Author name content?: string; // Full HTML content contentSnippet?: string; // Plain text content snippet summary?: string; // Summary/alternative content guid?: string | { // Global unique identifier _: string; $?: { isPermaLink?: string } }; enclosure?: { // Media enclosure url?: string; length?: string; type?: string; }; categories?: any[]; // Categories itunes?: any; // Podcast metadata [key: string]: any; // Custom fields }

Custom Fields

Extract custom XML fields from feeds:

const parser = new Parser({ customFields: { feed: ['dc:creator', 'dc:date', 'sy:updatePeriod'], item: ['dc:subject', 'media:thumbnail', 'content:encoded'] } }); const feed = await parser.parseURL('https://example.com/feed.xml'); // Access custom fields console.log(feed['dc:creator']); console.log(feed['dc:date']); feed.items.forEach(item => { console.log(item['dc:subject']); console.log(item['media:thumbnail']); });

Podcast Support

The parser automatically extracts iTunes podcast metadata:

const feed = await parser.parseURL('https://example.com/podcast.xml'); // Feed-level iTunes metadata console.log(feed.itunes?.author); // Podcast author console.log(feed.itunes?.subtitle); // Short subtitle console.log(feed.itunes?.summary); // Detailed summary console.log(feed.itunes?.explicit); // Explicit content flag console.log(feed.itunes?.keywords); // Keywords array console.log(feed.itunes?.image); // Podcast image URL console.log(feed.itunes?.owner); // Owner information console.log(feed.itunes?.categories); // Categories with subcategories // Item-level iTunes metadata feed.items.forEach(item => { console.log(item.itunes?.author); // Episode author console.log(item.itunes?.subtitle); // Episode subtitle console.log(item.itunes?.summary); // Episode summary console.log(item.itunes?.duration); // Episode duration console.log(item.itunes?.explicit); // Explicit content flag console.log(item.itunes?.keywords); // Keywords console.log(item.itunes?.image); // Episode image URL });

Format Detection

The parser automatically detects the feed format:

  • Atom: result.feed exists
  • RSS 2.0: result.rss.$.version starts with “2”
  • RSS 1.0: result["rdf:RDF"] exists
  • RSS 0.9: result.rss.$.version matches “0.9”

Default RSS Version

For unversioned RSS feeds, specify a default version:

const parser = new Parser({ defaultRSS: 2 // Treat unversioned RSS as RSS 2.0 });

Error Handling

Handle parsing errors gracefully:

import { Parser } from 'rss.today'; const parser = new Parser(); try { const feed = await parser.parseURL('https://example.com/feed.xml'); console.log(feed.title); } catch (error) { if (error.message.includes('Feed not recognized')) { console.error('Unsupported feed format'); } else if (error.message.includes('Status code')) { console.error('HTTP error:', error.message); } else if (error.message.includes('File not found')) { console.error('File does not exist'); } else if (error.message.includes('Invalid XML')) { console.error('Malformed XML'); } else { console.error('Error:', error.message); } }

Practical Examples

Fetch and Display Feed

import { Parser } from 'rss.today'; async function displayFeed(url: string, itemLimit: number = 10) { const parser = new Parser(); const feed = await parser.parseURL(url); console.log(`=== ${feed.title} ===`); console.log(feed.description); console.log(`Link: ${feed.link}`); console.log(`Language: ${feed.language}`); console.log(`Items: ${feed.items.length}\n`); feed.items.slice(0, itemLimit).forEach((item, index) => { console.log(`${index + 1}. ${item.title}`); console.log(` ${item.link}`); if (item.pubDate) { console.log(` Published: ${item.pubDate}`); } if (item.author) { console.log(` Author: ${item.author}`); } console.log(); }); } displayFeed('https://example.com/feed.xml');

Extract Podcast Episodes

import { Parser } from 'rss.today'; async function getPodcastEpisodes(feedUrl: string) { const parser = new Parser(); const feed = await parser.parseURL(feedUrl); if (!feed.itunes) { console.log('Not a podcast feed'); return; } console.log(`Podcast: ${feed.title}`); console.log(`Author: ${feed.itunes.author}`); console.log(`Total Episodes: ${feed.items.length}\n`); feed.items.forEach((item, index) => { console.log(`Episode ${index + 1}: ${item.title}`); console.log(`Duration: ${item.itunes?.duration || 'Unknown'}`); console.log(`Published: ${item.pubDate}`); console.log(`Audio: ${item.enclosure?.url}`); console.log(); }); } getPodcastEpisodes('https://example.com/podcast.xml');

Parse with Custom Fields

import { Parser } from 'rss.today'; async function parseWithCustomFields(xml: string) { const parser = new Parser({ customFields: { feed: ['media:credit', 'media:copyright'], item: ['media:content', 'media:thumbnail'] } }); const feed = await parser.parseString(xml); console.log('Custom feed fields:'); console.log(feed['media:credit']); console.log(feed['media:copyright']); feed.items.forEach(item => { console.log(`Item: ${item.title}`); console.log('Media content:', item['media:content']); console.log('Thumbnail:', item['media:thumbnail']); }); }
Last updated on