Skip to content

HTML Method ​

The HTML method extracts feed URLs from HTML content by scanning link elements and anchor tags.

Follows RSS Board Autodiscovery and WHATWG Feed Autodiscovery specs.

How It Works ​

The HTML method scans two types of elements:

Looks for <link> elements that advertise feeds:

html
<!-- rel="alternate" with feed MIME type -->
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />

<!-- rel="feed" (WHATWG spec) -->
<link rel="feed" href="/feed" />

Anchor Elements ​

Scans <a> tags for feed links using two strategies:

  1. URI matching: Checks if href ends with a common feed path like /feed or /rss.xml, or has a feed segment like /rss/ in its path.
  2. Label matching: Checks if the link text, title, or aria-label contains words like "RSS", "Feed", "Atom". This catches icon-only links that have no visible text.
html
<!-- Matched by URI -->
<a href="/feed.xml">XML</a>

<!-- Matched by label (text) -->
<a href="/subscribe">RSS Feed</a>

<!-- Matched by label (title / aria-label) on an icon-only link -->
<a href="/blog/syndication" title="RSS feed"><svg>...</svg></a>

Configuration ​

Feedscout comes with reasonable defaults, but you can customize how HTML is parsed if needed.

Control which <link> elements are matched:

typescript
import { mimeTypes } from 'feedscout/feeds'

const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      linkSelectors: [
        { rel: 'alternate', types: mimeTypes },
        { rel: 'feed' },
      ],
    },
  },
})

Anchor URIs ​

Specify URI patterns to match at the end of anchor href attributes:

typescript
const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      anchorUris: ['/feed', '/rss', '/atom', '/rss.xml', '/feed.xml'],
    },
  },
})

Anchor Labels ​

Specify text patterns to match in anchor content:

typescript
const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      anchorLabels: ['rss', 'feed', 'atom', 'syndication'],
    },
  },
})

Anchor Attributes ​

Specify element attributes to scan for the anchor labels, on the anchor itself and on its descendants. This finds icon-only feed links whose label lives in an attribute, not in visible text: a title, an aria-label, or the layer name Framer emits on a feed icon (data-framer-name="RSS Icon"):

typescript
const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      anchorAttributes: ['aria-label', 'title', 'data-framer-name'],
    },
  },
})

Anchor Path Segments ​

Specify patterns to match against the pathname of anchor href attributes. Only the pathname is checked, so a feed path inside a query string does not count:

typescript
const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      anchorPathSegments: [/\/rss\//, /\/atom\//, /\/feed\//],
    },
  },
})

Ignored URIs ​

Exclude certain URI patterns from anchor matching:

typescript
const feeds = await discoverFeeds(url, {
  methods: {
    html: {
      anchorIgnoredUris: ['wp-json/oembed/', 'wp-json/wp/'],
    },
  },
})

Default Values ​

You can import the default HTML options:

typescript
import { defaultHtmlOptions } from 'feedscout/feeds'

The defaults include comprehensive anchor URIs and common feed-related labels. You can also import individual pieces:

typescript
import {
  linkSelectors,
  anchorLabels,
  anchorAttributes,
  anchorPathSegments,
  urisComprehensive,
  ignoredUris,
} from 'feedscout/feeds'

Using Directly ​

You can use the HTML discovery function directly to get URIs without validation:

typescript
import { discoverUrisFromHtml } from 'feedscout/methods'

const uris = discoverUrisFromHtml(htmlContent, {
  baseUrl: 'https://example.com',
  linkSelectors: [{ rel: 'alternate', types: ['application/rss+xml'] }],
  anchorUris: ['/feed'],
  anchorLabels: ['rss'],
  anchorIgnoredUris: [],
})

// [
//   '/feed.xml',
//   '/rss',
// ]

URIs come back as written in the HTML, so they can be relative. They are resolved against <base href> when the page has one. discoverFeeds resolves the rest against the page URL.