---
title: "How to post news from an RSS feed in a Discord channel | StreetHosting"
description: "Post news from an RSS feed in a Discord channel with rss-parser, without repeating items, using embeds and polite polling. Includes the webhook option."
url: "https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news"
type: "page"
language: "en-US"
---

Discord Bots · 7 min · Intermediate

Published on Nov 13, 2026 · Updated on Nov 13, 2026

# Discord bot with an RSS feed: automatic news in a channel

Learn to read an RSS feed with the rss-parser library, save the last item seen, poll without overloading the site and post each story in an embed with a title and link.

By [Equipe StreetHosting](https://streethosting.com.br/en/autores#equipe-streethosting) · StreetHosting infrastructure and support team

[Automation and webhooks](https://streethosting.com.br/en/guides/topics/automation)

Summarize with:

[](https://chat.openai.com/?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "ChatGPT") [](https://claude.ai/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Claude") [](https://www.google.com/search?udm=50&aep=11&q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Google AI Mode") [](https://x.com/i/grok?text=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Grok") [](https://www.perplexity.ai/search/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Perplexity")

Share:

[](https://x.com/intent/tweet?text=How%20to%20post%20news%20from%20an%20RSS%20feed%20in%20a%20Discord%20channel&url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news "Share on X") [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news "Share on Facebook") [](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news "Share on LinkedIn") [](https://wa.me/?text=How%20to%20post%20news%20from%20an%20RSS%20feed%20in%20a%20Discord%20channel%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fdiscord-bots%2Fdiscord-bot-rss-feed-news "Share on WhatsApp")

For agents: Copy as Markdown [.md](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news.md)

In this guide 7 sections

* [How a feed becomes a message](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#como-funciona)
* [Read the feed with rss-parser](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ler-feed)
* [Save the last item seen](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ultimo-visto)
* [Poll politely](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#intervalo-educado)
* [The full bot with an embed](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#bot-completo)
* [Alternative without a bot: webhook](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#webhook-sem-bot)
* [Where to run it at StreetHosting](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#hospedar)

Quick answer

Install `rss-parser`, read the feed every 10 to 30 minutes, compare the `guid` of each item with the ones already seen and post only the new ones in an embed with a title and link. Save the seen items to a file so the bot does not repeat a story after a restart. Without a bot, you can use a webhook, as long as something polls the feed and triggers the message.

## How a feed becomes a message[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#como-funciona)

An RSS feed is a file that a site updates every time it publishes something new. Inside it there is a list of items, each with a title, link, date and summary. Your bot downloads that file every so often, works out which items it has not posted yet and sends each one as a message in the channel.

The work splits into three parts: read the feed, remember what was already posted and build the message. This guide uses discord.js v14 and Node 20 or newer. If you do not have a bot yet, start with the guide on [creating your first Discord bot from scratch](https://streethosting.com.br/en/guides/discord-bots/create-discord-bot). The bot only sends messages, so the Guilds intent is enough.

A note on content. Post the title, a short summary and the link to the original text. Copying the whole article into Discord is not polite to the people who wrote it and can cause copyright trouble. The link sends the audience back to the site.

## Read the feed with rss-parser[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ler-feed)

Install the library with `npm install rss-parser`. It downloads the file, understands RSS and Atom and returns the items already organized.

`const Parser = require('rss-parser'); const parser = new Parser(); async function teste() { const feed = await parser.parseURL('https://exemplo.com/feed.xml'); console.log(feed.title); for (const item of feed.items.slice(0, 3)) { console.log(item.title, item.link, item.guid, item.isoDate); } } teste();`

Run this before touching Discord. Each item carries `title`, `link`, `guid`, `isoDate` and `contentSnippet`, which is the summary without HTML. In an Atom feed the identifier comes in `id` instead of `guid`. If you get an error saying the content does not look like a feed, the address probably points to a regular page and not to the feed file.

## Save the last item seen[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ultimo-visto)

The identifier of each item is its **guid** in RSS feeds and its **id** in Atom feeds. Some feeds do not fill in that field, so use the link as the last option, as the `chave` function in the full code does. The rule is simple: an item is new when its identifier is not in the list of ones already seen.

Why a list and not just the last id? Because the order of items in the feed is not always what you expect, and because the site may delete the most recent story. With a list of the last 100 identifiers, the bot keeps getting it right in those cases.

* On the first run of a feed, memorize all the current items without posting any.
* On later runs, post only what is not in the list.
* Post from oldest to newest, so the channel stays in chronological order.
* Save the list to a file, so it survives a restart.

Some sites change a story's guid when they edit it. If your bot starts repeating articles, switch the key to the item link. In most cases the link is more stable.

## Poll politely[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#intervalo-educado)

The feed belongs to a site that pays for the traffic. A bot that downloads the file every few seconds bothers that site and may get blocked. A few good rules:

* Use an interval of 10 to 30 minutes. For most news sites that is more than enough.
* Identify the bot in the `User-Agent` header, with a name and a contact, so the site owner knows who is visiting.
* Use conditional requests: save the `ETag` and `Last-Modified` of the last response and send them back. If nothing changed, the site answers 304 without sending the file.
* Set a timeout on the request, so a slow site does not freeze your bot.
* Poll feeds one at a time, not all at the same moment.

To use conditional requests, download the file with `fetch` and hand the text to the parser with `parseString`. That is what the full code in the next section does.

## The full bot with an embed[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#bot-completo)

The code below puts it all together: several feeds, each with its own channel, a list of seen items saved to a file, conditional requests and one embed per story with a clickable title, a short summary and the date. To polish the embed further, also see the guide on [embeds and formatted messages](https://streethosting.com.br/en/guides/discord-bots/discordjs-embed-formatted-messages).

`` const fs = require('node:fs'); const Parser = require('rss-parser'); const { Client, Events, GatewayIntentBits, EmbedBuilder } = require('discord.js'); const FEEDS = [ { nome: 'Site de exemplo', url: 'https://exemplo.com/feed.xml', canalId: process.env.CANAL_NOTICIAS_ID, }, ]; const ARQUIVO_ESTADO = './estado-feeds.json'; const INTERVALO = 15 * 60 * 1000; const parser = new Parser(); const client = new Client({ intents: [GatewayIntentBits.Guilds] }); function lerEstado() { try { return JSON.parse(fs.readFileSync(ARQUIVO_ESTADO, 'utf8')); } catch { return {}; } } function salvarEstado(estado) { fs.writeFileSync(ARQUIVO_ESTADO, JSON.stringify(estado, null, 2)); } const chave = (item) => item.guid || item.id || item.link; async function baixarFeed(url, cache) { const headers = { 'User-Agent': 'MeuBotNoticias/1.0 (contato: voce@exemplo.com)' }; if (cache.etag) headers['If-None-Match'] = cache.etag; if (cache.modified) headers['If-Modified-Since'] = cache.modified; const resp = await fetch(url, { headers, signal: AbortSignal.timeout(15000) }); if (resp.status === 304) return null; if (!resp.ok) throw new Error(`Feed respondeu ${resp.status}`); return { dados: await parser.parseString(await resp.text()), etag: resp.headers.get('etag'), modified: resp.headers.get('last-modified'), }; } function montarEmbed(feed, item) { const embed = new EmbedBuilder() .setColor(0xff6a00) .setTitle((item.title || 'Sem título').slice(0, 256)) .setURL(item.link) .setFooter({ text: feed.nome }); if (item.contentSnippet) { embed.setDescription(item.contentSnippet.slice(0, 300)); } if (item.isoDate) embed.setTimestamp(new Date(item.isoDate)); return embed; } async function checarFeed(feed, estado) { const cache = estado[feed.url] ?? (estado[feed.url] = { vistos: [] }); const baixado = await baixarFeed(feed.url, cache); if (!baixado) return; // 304: nada mudou const primeiraVez = cache.vistos.length === 0; const novos = baixado.dados.items.filter( (item) => chave(item) && !cache.vistos.includes(chave(item)) ); const canal = await client.channels.fetch(feed.canalId); for (const item of novos.reverse()) { if (!primeiraVez) await canal.send({ embeds: [montarEmbed(feed, item)] }); cache.vistos.unshift(chave(item)); } cache.vistos = cache.vistos.slice(0, 100); cache.etag = baixado.etag; cache.modified = baixado.modified; } let rodando = false; async function rodar() { if (rodando) return; rodando = true; const estado = lerEstado(); for (const feed of FEEDS) { try { await checarFeed(feed, estado); } catch (err) { console.error(`Erro no feed ${feed.nome}:`, err.message); } finally { salvarEstado(estado); } } rodando = false; } client.once(Events.ClientReady, () => { rodar(); setInterval(rodar, INTERVALO); }); client.login(process.env.DISCORD_TOKEN); ``

Two cautions in the code. The `slice` on the texts respects the embed limits, which allow up to 256 characters in the title and 4096 in the description. And the ETag is only updated after everything is posted, so if sending fails halfway, the next poll tries again instead of assuming there is nothing new.

Want one story per plain message, with no embed? Swap the send for `canal.send(item.link)`. Discord builds the link preview by itself, and the result works well in news channels.

## Alternative without a bot: webhook[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#webhook-sem-bot)

If you do not want to keep a bot just for this, Discord lets you create a webhook in the channel. In the channel settings, under Integrations, create a webhook and copy the address. Any program that makes a POST to that address publishes a message in the channel, with no bot login and no intents. The guide on the [Discord webhook for alerts](https://streethosting.com.br/en/guides/discord-bots/discord-webhook-server-alerts) shows the format in more detail.

`await fetch(process.env.WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ embeds: [{ title: (item.title || 'Sem título').slice(0, 256), url: item.link, description: (item.contentSnippet || '').slice(0, 300), color: 0xff6a00, }], }), });`

The catch is that the webhook only receives, it does not poll the feed. Something has to read the RSS and run the new items logic. It can be the same script from this guide, swapping the `canal.send` for the POST above and running it on a schedule, or a ready made n8n automation, which has an RSS node and a Discord node. If you go that way, the guide on [hosting n8n on a VPS](https://streethosting.com.br/en/guides/vps/host-n8n-vps-brazil) helps. The downside of the webhook is that you lose commands, buttons and any interaction. For just pushing news, it does the job.

## Where to run it at StreetHosting[](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#hospedar)

The news bot has to stay on, because its process is what polls the feeds on the interval. On the [StreetHosting bot hosting](https://streethosting.com.br/en/bots) it runs in a Node.js container with a panel, and the Discord Essential plan, at R$ 9.99/month with 1 GB of RAM, is more than enough for this kind of bot, which spends almost all its time idle waiting for the next interval.

Keep the state file in the bot folder and set the environment variables in the panel. For the same polling pattern with a different data source, see the guide on [Twitch and YouTube live notifications](https://streethosting.com.br/en/guides/discord-bots/discord-bot-twitch-youtube-live-notifications). And if you want to understand the hosting options, read [hosting a Discord bot 24/7](https://streethosting.com.br/en/guides/discord-bots/discord-bot-hosting-24-7).

In this guide

* [How a feed becomes a message](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#como-funciona)
* [Read the feed with rss-parser](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ler-feed)
* [Save the last item seen](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#ultimo-visto)
* [Poll politely](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#intervalo-educado)
* [The full bot with an embed](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#bot-completo)
* [Alternative without a bot: webhook](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#webhook-sem-bot)
* [Where to run it at StreetHosting](https://streethosting.com.br/en/guides/discord-bots/discord-bot-rss-feed-news#hospedar)

## Frequently asked questions

What is an RSS feed and where do I find its address?

It is a file the site maintains with a list of its latest content, in a format that is easy for programs to read. The address usually ends in feed, rss or feed.xml, and many sites show an RSS icon or link in the footer. If you cannot find it, search the page source for rss or atom.

What polling interval should I use?

For news, between 10 and 30 minutes is a polite interval. Polling every few seconds does not make the alert faster, because most sites update the feed every so often, and it can get your bot blocked.

Why did the bot repeat old stories after a restart?

Because the record of seen items lived only in memory. Save it to a file or a database, and on the first run just memorize the current items without posting, so the bot does not dump the whole feed into the channel.

Does rss-parser read Atom feeds too?

Yes. The library understands RSS and Atom and returns the items in a similar format in both cases, with fields like title, link and isoDate. The difference is the identifier: it comes in guid for RSS feeds and in id for Atom feeds.

Can I post an RSS feed to Discord without creating a bot?

You can, using a channel webhook. But the webhook only receives messages, it does not poll the feed by itself. You still need something that checks the RSS and makes the call, such as a scheduled script or an n8n automation.

Next step

See bot plans

24/7 Discord bots with fast activation and support in Portuguese.

[See bot plans](https://streethosting.com.br/en/bots)

## Related guides

[Discord bots Intermediate How to announce Twitch and YouTube live streams in Discord Learn how to query the Twitch Helix API and the YouTube RSS feed, build the announcement embed and remember what was already announced so the bot never repeats a message. 8 min Read guide](https://streethosting.com.br/en/guides/discord-bots/discord-bot-twitch-youtube-live-notifications) [Discord bots Beginner How to use a Discord webhook for server alerts A Discord webhook is a quick way to notify your team about incidents, deploys and outages without relying on manual monitoring. 2 min Read guide](https://streethosting.com.br/en/guides/discord-bots/discord-webhook-server-alerts) [Discord bots Beginner Discord embeds with discord.js: how to build formatted messages The embed is the colored card that gives your bot's messages a professional look. Learn to build one from scratch, the official limits, what markdown works inside it and how to edit an embed that was already sent. 7 min Read guide](https://streethosting.com.br/en/guides/discord-bots/discordjs-embed-formatted-messages)

[← Back to the Guide Center](https://streethosting.com.br/en/guides)
