Skip to main content

RSS in 2025 lives in Telegram

Constantin Potapov
10 min

Google Reader shut down in 2013. The XML did not die. It became a pipe. The face of the pipe is a channel.

RSS in 2025 lives in Telegram

In 2013 Google shut Google Reader. The press wrote obituaries for XML nobody needed. The corpse kept working.

Twelve years later the same feed is read in Telegram and nobody knows it is a feed. The orange icon is gone. The blue paper plane sits in its place.

RSS stopped being a user problem. Like coffee: the taste matters, not the pH.

Why readers lost and the protocol did not

Aggregators of the 2000s were right and unusable. A separate app, the idea of a "feed", a link hidden in <head>, an evening on folders and tags. People wanted to tap and read.

Subscriptions were yours alone. You read a strong piece: nobody knows. No reactions, no repost. An advertiser has no one to target. Noble and without a till.

RSS aggregator (2010)
Telegram channel (2025)
Subscribe time
5 min (find RSS, add)
1 click (channel link)
71900%
Interaction
Read-only
Reactions, comments, reposts
Analytics
Feed download counter
Reach, views, engagement
Monetization
Hard to track ROI
Direct ads, subscriptions

Telegram solved the same jobs and never said the word RSS. The app is already on a parent's phone. Reactions and comments out of the box. The push knocks by itself. Reach and views are visible to the author. Charity became a channel you can sell.

The bridge

A bot looks at the feed every five minutes, finds what is new, formats it, puts it in the channel.

Ready-made @RobotRSSBot. IFTTT at $2.5. Zapier from $20. n8n or Make if you want your own server. Your own script: Python, feedparser, python-telegram-bot.

A site with a feed becomes a channel once. Then years without copying by hand.

# Python example (simplified)
import feedparser
import asyncio
from telegram import Bot
 
RSS_URL = "https://example.com/feed.xml"
TELEGRAM_CHANNEL = "@my_channel"
CHECK_INTERVAL = 300  # 5 minutes
 
bot = Bot(token="YOUR_BOT_TOKEN")
seen_entries = set()
 
async def check_feed():
    feed = feedparser.parse(RSS_URL)
 
    for entry in feed.entries[:5]:  # Last 5 entries
        entry_id = entry.get('id') or entry.link
 
        if entry_id not in seen_entries:
            # Format message
            message = f"<b>{entry.title}</b>\n\n"
            message += f"{entry.summary[:300]}...\n\n"
            message += f"🔗 <a href='{entry.link}'>Read more</a>"
 
            # Send to channel
            await bot.send_message(
                chat_id=TELEGRAM_CHANNEL,
                text=message,
                parse_mode='HTML'
            )
 
            seen_entries.add(entry_id)
 
# Run check loop
while True:
    await check_feed()
    await asyncio.sleep(CHECK_INTERVAL)

Then usually: images via send_photo(), seen_entries in SQLite or Redis, a dead feed and rate limits handled, a keyword filter, hashtags.

Why this is not only for geeks

A channel with no original text. Five to ten feeds you already read with morning coffee. A bridge on each. Ten minutes a day throwing out trash. Recruiters in the niche pay for the audience, courses pay a cut, once a week you can sell your own note.

$0
content creation cost
2 hours
setup time
10-20%
ad commission
24/7
automatic operation

A competitor shipped three features while you planned a sprint. Their blog into a private team channel. Keywords: release, partnership, layoffs. You must not copy their text into a public channel. Inside, you can look.

Your Medium, YouTube and GitHub releases also speak RSS: medium.com/feed/@username, youtube.com/feeds/videos.xml?channel_id=XXX, github.com/user/repo/releases.atom. Three bridges, one door.

A B2B digest: industry feeds, a filter, five stories a week with a comment. Mail hits spam. A channel does not. Email open rate 15-25%, a channel 60-80%.

Email newsletter
Telegram channel
Open Rate
15-25%
60-80%
300%
CTR
2-3%
8-12%
300%
Spam filters
Yes
No

Inside a company the same pipe takes deploys, releases, Confluence edits and closed Jira tickets. History stays in the chat. No all-hands email.

What you see and what you do not

A feed shows XML download counts, a user-agent, and a request at 03:42. It does not show who subscribed, whether they finish the piece, or which entry is alive. Bots download too.

Telegram shows reach, views, reposts, reactions, growth and hours of activity.

Hypothesis test:
- Publish at 9:00 AM → 1000 views
- Publish at 6:00 PM → 3000 views
Conclusion: Audience is active in the evening, set the RSS bridge to evening

The feed gives <title>Update: New Features</title>. The bot writes a human headline. You compare CTR.

# RSS gives link: https://blog.com/post
# Bot adds UTM:
link = f"{entry.link}?utm_source=telegram&utm_medium=rss_bot&utm_campaign=auto"

Then a funnel in analytics: publish, click, conversion.

Traps

pubDate sometimes jumps on an old entry. I do not lean on the date. I take the id and a hash of the body.

# Don't rely only on pubDate
# Use unique ID + content hash
import hashlib
 
def get_entry_hash(entry):
    content = entry.title + entry.link + entry.get('summary', '')
    return hashlib.md5(content.encode()).hexdigest()
 
seen_hashes = set()
 
for entry in feed.entries:
    entry_hash = get_entry_hash(entry)
    if entry_hash not in seen_hashes:
        # Publish
        seen_hashes.add(entry_hash)

Bot API: 30 messages a second to one channel. Two seconds between posts, or an hourly digest.

import asyncio
 
async def publish_with_delay(entries):
    for entry in entries:
        await bot.send_message(...)
        await asyncio.sleep(2)  # 2 seconds between posts

Feeds contain broken HTML.

from bs4 import BeautifulSoup
import html
 
def clean_html(text):
    # Parse HTML
    soup = BeautifulSoup(text, 'html.parser')
 
    # Remove tags, keep text
    text = soup.get_text()
 
    # Escape special characters for Telegram HTML
    text = html.escape(text)
 
    return text[:500]  # Trim to 500 characters

Often there is no image. I take the first from content:encoded or description.

# Parse first image from <content:encoded> or <description>
soup = BeautifulSoup(entry.content[0].value, 'html.parser')
img = soup.find('img')
 
if img and img.get('src'):
    await bot.send_photo(
        chat_id=CHANNEL,
        photo=img['src'],
        caption=message,
        parse_mode='HTML'
    )
else:
    await bot.send_message(...)  # Without image

A protocol, not a product

Podcasts are still RSS inside. YouTube gives a feed per channel. Substack generates one next to the email. Notion and Obsidian Publish breathe the same XML.

A lazy API: no token, CDN cache, a schema that will outlive the next fashionable JSON.

Big media copy the feed into a channel. An editor does not retype every headline. Often 80% automatic, 20% a hand.

2005: RSS → Google Reader
2013: Google Reader shut down
2020: RSS-to-Telegram bots
2025: people read RSS and call it Telegram

Find a feed: /feed, /rss, /atom.xml, a Chrome extension, rss.app, feed43.com. A channel, a bot via @BotFather, the bot as admin with post rights. Either @RobotRSSBot and /add, or a script on a VPS and systemd, or IFTTT.

RSS survived Google Reader. Telegram gave it a face. If the content already speaks a feed, there is no reason to type it a second time by hand.