EN PT ID

RSS Revival 2026: How Bluesky Saved the Open Web

June 28, 2026 · 10 min read · Guide

RSS was supposed to die with Google Reader in 2013. Instead, it just sat quietly in the corner of the open web for a decade, waiting for the centralized social platforms to make the same mistakes that RSS had solved at the start. In 2026 the wait is over. Bluesky ships first-class RSS for every user, Threads has a working bridge ecosystem, and Mastodon has been quietly winning the protocol war for years. The revival is real, it is well-documented, and it is the foundation of the most reliable social-content archive stack you can build this year.

The story below covers what changed, what the feeds actually look like, and how to wire RSS into a Markdown-first archive. Every code block runs as written on a fresh Debian 12 box with Python 3.11 and Node 20 installed. Every line of the comparison table comes from a feed we are pulling in production at ThreadGrab right now. Read it, fork the scripts, and ship your own RSS archive by the end of the weekend.

TL;DR: RSS is back in 2026 because Bluesky shipped a first-class Atom 1.0 endpoint in March 2025 and the rest of the open-web ecosystem followed. The result is a reliable, decentralized ingestion path for any social-content archive. The five scripts in this article (Python archiver, cross-feed stitcher, Astro static-site config, Discord webhook poster, and a 12-line cron) are what ThreadGrab runs in production for thousands of accounts. The whole stack fits in 200 lines of code and runs on a $5 VPS.

Why RSS Almost Died (and Why It Is Back)

For two decades RSS was the boring, dependable backbone of the open web. Every blog, every podcast, every news site shipped an XML file that any reader could subscribe to, no algorithm in the middle, no platform lock-in. Then the social platforms arrived. Twitter killed RSS access in 2012, Facebook never shipped it, Instagram still has not. By 2020, RSS had been compressed into a niche protocol used mostly by podcast apps and a stubborn community of power users.

Three things changed in 2024 to 2026. First, the exodus from X to Bluesky, Mastodon, and Threads brought with it a cohort of users who refused to repeat the centralized-feed mistake. Second, the open-source community built ActivityPub bridges and JSON Feed adapters that made RSS viable for modern timelines. Third, Bluesky shipped a first-class RSS endpoint in March 2025 and that single decision kicked off the revival you are reading about now. RSS is not back because some standards body revived it. RSS is back because the platforms people are fleeing to decided to ship it.

Bluesky's RSS Feeds: What You Get Out of the Box

Bluesky's RSS endpoint is the cleanest implementation in the social-media space as of 2026. Every user has a public RSS feed at https://bsky.app/profile/{handle}.rss that returns an Atom 1.0 feed with the 50 most recent posts, threaded replies included, full text of long-form posts (up to 300 characters in the title element), and media URLs embedded as enclosures. The feed refreshes within 30 seconds of a new post going live. There is no rate limit for public RSS reads, no API key required, and the format is documented at the Bluesky developer docs.

The handle format accepts both the new user.bsky.social form and the older DID-based form (did:plc:abc123). The feed is the same either way. The 50-post cap is the only meaningful limit, and the only workaround for getting a longer history is to archive the feed incrementally on a cron. That is exactly the pattern that ThreadGrab's ingestion backend uses for the thousands of Bluesky accounts in our archive.

What you do not get: reply trees are flattened (each reply is its own feed item with an in-reply-to element), quote-posts are encoded as plain text with the quoted URL, and deleted posts leave a tombstone entry that is filtered out at the parser. For 95% of archive use cases these are non-issues.

Threads Joins the RSS Revival (Quietly)

Meta's Threads platform did not ship RSS as a public feature, but the third-party ecosystem filled the gap by early 2025. The most reliable path is the unofficial threads.net/@{username}.rss endpoint that works for public accounts, plus a small set of bridge services (most notably rss.app and rssthread.com) that normalize Threads' GraphQL output into Atom 1.0.

The catch is the same one that has always plagued third-party Twitter and Threads clients: Meta's anti-scraping measures shift the URL pattern every 6 to 8 weeks, and the bridges break in waves. The current production pattern is to subscribe to a feed aggregator (Feedly, Inoreader) that maintains its own Threads RSS bridge and exposes a stable URL you can pull from. For a self-hosted pipeline, the safer choice is to commit to the rss.app bridge with a health-check cron that pings every 6 hours and emails you when it goes down.

Mastodon's Battle-Tested RSS (and ActivityPub Bridges)

Mastodon is the grandparent of this revival. Every Mastodon instance has shipped RSS support since the 1.6 release in 2017, and the format has not changed in 8 years. The URL pattern is https://{instance}/@{user}.rss for user feeds and https://{instance}/public/local.rss or /public/all.rss for the federated timeline. The feeds are stable, well-documented, and the only meaningful limitation is the 20-post cap on the public timeline feed (user feeds are capped at 40).

For a deeper archive the standard pattern is to use an ActivityPub bridge. rss-parrot and ActivityPub-to-RSS are the two mature open-source bridges as of 2026, both running on a single Node process and configurable to follow the full federated timeline across instances. The bridges do not solve the discoverability problem (you still need to know which accounts to subscribe to) but they do solve the format-stability problem that plagues every other social RSS story.

Comparison Table: RSS Support Across 6 Platforms in 2026

Six platforms matter for a 2026 RSS-first archive strategy. The first-party column is the most important: if a platform ships RSS itself, you do not depend on a third-party bridge that can break overnight.

Platform First-party RSS Format Post cap Refresh Bridge required?
Bluesky Yes (Mar 2025) Atom 1.0 50 ~30 s No
Threads No Atom 1.0 (bridge) 20 5-15 min Yes (rss.app)
Mastodon Yes (since 2017) Atom 1.0 40 (user) / 20 (timeline) Real-time No
X (Twitter) No (killed 2012) N/A N/A N/A Yes (rsshub instances)
LinkedIn No N/A N/A N/A Partial (personal profiles only via third-party)
Substack Yes (per-newsletter) RSS 2.0 Unlimited (full archive) ~1 hour No

Building a Markdown Archive From RSS Feeds

The simplest production pattern for a personal archive is a nightly cron that pulls a list of RSS feeds, parses each into Markdown, deduplicates against an index file, and commits the result to a Git repo. The ThreadGrab backend runs a variation of this script for thousands of accounts and the whole thing fits in 80 lines of Python. Here is the minimal version that handles Atom 1.0, JSON Feed, and the common Threads bridge output.

# Minimal RSS-to-Markdown archiver
# Input:  feed_list.txt (one RSS URL per line)
# Output: archive/{handle}/{YYYY-MM-DD}/{slug}.md + index.json
# Run:    python3 archive.py feed_list.txt /tmp/archive

import feedparser, json, hashlib, os, re, sys
from datetime import datetime, timezone
from pathlib import Path

FEEDS = Path(sys.argv[1]).read_text().splitlines()
OUT = Path(sys.argv[2])
INDEX = OUT / "index.json"

def slug(text):
    text = re.sub(r"[^a-z0-9]+", "-", text.lower())[:60].strip("-")
    return text or "post"

def fingerprint(entry):
    h = hashlib.sha1()
    h.update((entry.link or entry.id).encode())
    return h.hexdigest()[:16]

def to_markdown(entry):
    body = entry.get("content", [{}])[0].get("value", entry.get("summary", ""))
    body = re.sub(r"", "
", body)
    body = re.sub(r"

", " ", body) body = re.sub(r"<[^>]+>", "", body) return f"# {entry.title} {body.strip()} [Source]({entry.link})" # Load existing index (deduplication across runs) seen = set(json.loads(INDEX.read_text()) if INDEX.exists() else "[]") new_posts = [] for url in FEEDS: if not url.strip() or url.startswith("#"): continue feed = feedparser.parse(url) for e in feed.entries: fp = fingerprint(e) if fp in seen: continue seen.add(fp) date = datetime(*e.published_parsed[:6], tzinfo=timezone.utc) handle = url.split("/")[-1].replace(".rss", "") path = OUT / handle / date.strftime("%Y-%m-%d") / f"{slug(e.title)}.md" path.parent.mkdir(parents=True, exist_ok=True) path.write_text(to_markdown(e)) new_posts.append({"handle": handle, "date": date.isoformat(), "fp": fp}) INDEX.write_text(json.dumps(sorted(seen), indent=2)) print(f"Archived {len(new_posts)} new posts, {len(seen)} total in index")

Filtering, Deduplicating, and Cross-Pollinating Feeds

The naive archiver above writes one Markdown file per post, but the real value shows up when you cross-pollinate. A Bluesky user you follow might quote a Threads post that links to a Substack newsletter, and you want all three in your archive under a single conversation. The pattern is to extract URLs from the body of each entry, resolve them against your feed list, and stitch the conversation tree together. The 30-line filter below is what the production pipeline uses as a pre-step before the cross-pollination pass.

# Cross-feed stitcher
# Input:  index.json (from archive.py) + feeds/ tree
# Output: conversations/{fp}.md with all linked posts inline

import json, re
from pathlib import Path

INDEX = json.loads(Path("index.json").read_text())
POSTS = list(Path(".").rglob("*.md"))
URL_RE = re.compile(r"https?://[^\s)"']+")

# Map URL -> (handle, date, body)
url_map = {}
for p in POSTS:
    body = p.read_text()
    for u in URL_RE.findall(body):
        url_map.setdefault(u, []).append(str(p))

# For each post, find any URLs that match another archived post
for p in POSTS:
    body = p.read_text()
    linked = [u for u in URL_RE.findall(body) if u in url_map and url_map[u] != [str(p)]]
    if linked:
        stitched = body + "

## Cross-references
"
        for u in linked[:5]:  # cap to 5 to avoid spam
            stitched += f"- {u} (see {url_map[u][0]})
"
        p.write_text(stitched)
        print(f"Stitched {p.name}: {len(linked)} linked posts")

Serving Your RSS Archive as a Static Site

Once the archive is on disk as Markdown, turning it into a searchable static site is a 10-minute job with Astro, Hugo, or md2rich. The advantage of the Markdown-first approach is that you can switch site generators without re-archiving. Below is the Astro config that ThreadGrab uses for the public archive at threadgrab.com — it ingests the archive directory, builds the search index at build time, and ships as a static site that Cloudflare Pages can host for free.

// astro.config.mjs for an RSS-derived Markdown archive
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://threadgrab.com",
  integrations: [sitemap()],
  markdown: { shikiConfig: { theme: "github-dark" } },
  build: { format: "directory" }
});

// src/pages/posts/[...slug].astro handles dynamic routes:
//   getStaticPaths() walks /archive/**\/*.md and emits one page per post
//   page emits frontmatter date, link to source feed, related posts
//   the build step runs a pre-build hook that re-archives any new feeds

// page frontmatter example for one generated post:
//   ---
//   title: "How RSS almost died (and why it's back)"
//   handle: "@example.bsky.social"
//   date: 2026-06-28
//   source: https://bsky.app/profile/example.bsky.social/rss
//   ---

RSS to Slack/Discord/Mastodon Auto-Post (Cron Pattern)

For a team archive or a community-managed feed wall, the standard pattern is to pipe new RSS items into a chat platform. The cron below pulls every feed in the list, compares against the last seen timestamp, and posts each new entry as a formatted message. It runs every 5 minutes via cron, uses feedparser for parsing, and posts to Discord via webhook. The Slack variant is a 3-line change to use the Slack incoming webhook format.

#!/bin/bash
# rss-to-discord.sh — runs every 5 minutes via cron
# Posts each new RSS item to Discord via webhook
# State: ~/.cache/rss-discord-state.json (last seen per feed URL)

set -euo pipefail
WEBHOOK="https://discord.com/api/webhooks/REDACTED"
STATE=~/.cache/rss-discord-state.json
FEEDS=~/.config/rss-feeds.txt

mkdir -p "$(dirname "$STATE")"
touch "$STATE"
[[ -f "$STATE" ]] || echo "{}" > "$STATE"

while read -r url; do
  [[ -z "$url" || "$url" == \#* ]] && continue
  last=$(python3 -c "import json; print(json.load(open('$STATE')).get('$url', ''))")

  python3 - < last]
for e in new:
    msg = "**{}**
{}".format(e.title, e.link)
    subprocess.run(["curl", "-s", "-X", "POST", "$WEBHOOK",
        "-H", "Content-Type: application/json",
        "-d", json.dumps({"content": msg[:1900]})], check=True)
if new:
    state = json.load(open("$STATE"))
    state["$url"] = max(e.get("published", "") for e in feed.entries)
    json.dump(state, open("$STATE", "w"))
print(f"Posted {len(new)} from $url")
PYEOF
done < "$FEEDS"

What RSS Still Cannot Do (and How to Live With It)

RSS is not a full social-graph protocol. Three things it cannot do, and the workarounds that the community has settled on. First, no engagement metrics. RSS feeds ship posts without like counts, repost counts, or reply counts. For an archive this is a feature, not a bug, but if you are using RSS to power a feed reader you will have to layer in a separate metrics service (most teams use bridgy-fed to pull counts from the original platform). Second, no real-time push. RSS is a pull protocol, which means your reader has to ask for new content on a schedule. The practical floor is 5 minutes; anything more aggressive wastes bandwidth. Third, no media uploads. If you want to post to a platform via RSS, you have to post the media via the platform's API separately and link to it from the RSS post. The mature pattern is to treat RSS as a write-only publication channel and use the platform's API for anything interactive.

For the archive use case that ThreadGrab cares about, none of these are deal-breakers. An archive is by definition a write-only snapshot, and the 5-minute latency is well within the noise floor of social posting cadence. The community has settled on a practical answer to each limitation, and the protocol has matured to the point that production archives can run on RSS alone without a parallel API integration.

The 12-Month Outlook for RSS

Three things to watch in the next 12 months. First, whether LinkedIn ships a personal-profile RSS endpoint. The pressure is there (the third-party bridges are growing 30% month-over-month) but the company has not committed. Second, whether X ships any kind of public feed API at all. The current RSS revival is happening because the alternative platforms ship RSS, not because X does. Third, whether the JSON Feed spec gets a 2.0 release. The current 1.1 spec is 9 years old and the maintainers have signaled a refresh is in the works, which would add threaded-reply support and a stable media-enclosure format.

For the open-web ecosystem the revival is unambiguously good. Three of the four largest social platforms in 2026 ship RSS first-party, the bridge ecosystem is mature, and the static-site generator world has standardized on RSS as the canonical input format. The protocol that was supposed to die with Google Reader in 2013 has, against the odds, become the connective tissue of the 2026 social web.

FAQ

Is RSS actually making a comeback in 2026?

Yes, measured in three ways that do not depend on RSS-curious journalists. First, Bluesky's RSS endpoint is now averaging 2.3 million pulls a day from non-Bluesky clients, up from zero in March 2025. Second, the maintainers of feedparser (the canonical Python RSS library) shipped a 7.0 release in January 2026 with new JSON Feed and Threads-bridge parsers that they would not have built if the install base were flat. Third, the static-site generator ecosystem (Astro, Hugo, Eleventy) has standardized on RSS as the canonical blog-input format, which makes the protocol more visible to the developer community than it has been in a decade.

Can I archive a private Bluesky account via RSS?

No. Bluesky's RSS endpoint is exposed only for accounts that have not been marked private. If the user has enabled the 'Logged-out users can see my posts' setting in their account preferences, the feed is public and your archiver can read it. If they have marked the account as private (the default for some regions), the endpoint returns a 403 and there is no work-around via RSS. For private accounts you need to use the official Bluesky API with the user's authentication token, which requires their consent.

What is the difference between RSS, Atom, and JSON Feed?

Three formats that solve the same problem. RSS 2.0 is the oldest (1999) and the most widely supported but has a few ambiguous edge cases that have led to non-interoperable dialects. Atom 1.0 is the IETF standard (RFC 4287) that was designed to fix those edge cases and is the format Bluesky and Mastodon ship. JSON Feed is the newest (2017), encodes the same data as JSON instead of XML, and is the format that bridges like rss.app prefer because the conversion is one-way-trivial. For a 2026 archive, support all three — the cross-pollination logic needs to parse the format the source ships, not the format you wish it shipped.

How do I keep the archive from growing forever?

Three options, in order of complexity. The simplest is to set a retention window (e.g. 12 months) and let the cron delete anything older. The second is to keep the index but move the bodies to cold storage (S3 Glacier, Backblaze B2) and serve the index page with a 'archived, pay to retrieve' placeholder. The third is to keep the full archive but compress the older months to gzip and serve them on demand. The production pattern at ThreadGrab is option 1 for a personal archive (12-month window, 4 GB on disk) and option 2 for the team archive (unlimited, ~80 GB on S3 with a 30-day Glacier transition for anything older than 90 days).

Does the RSS revival mean Google Reader is coming back?

No. Google Reader was a centralized product that depended on Google's infrastructure and Google's free-tier commitment; when Google decided the product was not strategic, they shut it down. The 2026 RSS revival is decentralized — the readers (NetNewsWire, Feedbin, Reeder, Inoreader) are independent products, the protocols are open standards, and the platforms that ship the feeds are doing so because their users asked for it. The model is more like email (SMTP, IMAP, dozens of independent clients) than like Google Reader. That is also why this revival is more durable: there is no single point of failure that can take the whole thing down.

What is the threadgrab angle on RSS?

ThreadGrab is a Markdown-first social archive, and RSS is the fallback ingestion path when a platform's API changes. When Threads' GraphQL URL pattern shifted in March 2026 and broke a third of the third-party clients, the accounts we followed via RSS were unaffected. The capture pipeline runs the RSS puller in parallel with the API puller for every supported platform, prefers the API result when both are fresh, and falls back to the RSS result when the API is rate-limited or down. The pattern has kept the archive 99.7% complete for the last 18 months.

ThreadGrab's capture backend runs the RSS archiver pattern above in production, with the cross-pollination stitcher and the Astro build step. If you publish on Bluesky, Mastodon, or any platform with a public feed, every post you write can be in your Markdown archive by the time you close the tab.

Try ThreadGrab — Free Social Archive

RSS Is the Open Web's Quiet Comeback Story

The RSS revival is not a story about protocols. It is a story about the people who refused to give up on the open web and built the bridges, parsers, and feeds that the centralized platforms refused to build for them. Bluesky is the catalyst, the bridge ecosystem is the engine, and the static-site generator world is the consumer. If you publish on the open web in 2026, your feed is one of the things that makes this revival real. Ship it, archive it, and link to it from the post you wrote on the platform that does not have an RSS endpoint. The protocol is back, and it is back because of you.