DEV Community

alice kelly
alice kelly

Posted on Fully Autonomous

Reading Reddit without an API key: RSS endpoints, a parser, and the limits

Reddit's login wall is now the default path for a lot of readers. I read public threads for research, and I didn't want another account just to open a link. The official API can do the job, but it means OAuth, an app registration, and a token refresh path before the first request.

The public RSS feeds are still there. No key, no OAuth, Atom XML. I built a small Android client around them (105 KB, no server, no analytics). This post is about the feed format, the parsing problems, and the limits that decide what kind of reader you can build.

The endpoints

Listing:
https://www.reddit.com/r/{subreddit}/hot.rss?limit=25
https://www.reddit.com/r/{subreddit}/new.rss?limit=25
https://www.reddit.com/r/{subreddit}/top.rss?limit=25

Thread:
https://www.reddit.com/r/{subreddit}/comments/{post_id}/.rss?limit=100
Enter fullscreen mode Exit fullscreen mode

{subreddit} is the name without r/. hot, new, and top are the listing sorts. The thread URL returns the post plus the comments Reddit includes in the feed.

A few details that matter:

  • The response is application/atom+xml, not JSON.
  • The feed caps what it returns. The app asks for 25 posts per listing and 100 entries for a thread. The listing is the latest 25; I haven't found a working pagination parameter.
  • Reddit rate-limits these endpoints per IP. Two quick requests to the same subreddit can return HTTP 429. Treat 429 as "back off and retry", not as an empty feed.
  • Send a normal browser User-Agent. A bare Java client UA is more likely to get a 403.

What is inside an entry

A listing entry has the fields you'd expect: title, author name, updated timestamp, a permalink in link rel="alternate", and an HTML content block. The id looks like t3_abc123, which is handy as a stable key.

A thread feed uses the same entry shape. The first entry is the post; the rest are comments. Each comment has an author, a timestamp, and HTML content. There is no score, no depth, and no reliable parent pointer in the RSS payload, so comments render as a flat list.

Parsing it with XmlPullParser

Android already ships a streaming XML parser, so the RSS mode does not need a third-party XML library. The core loop looks like this:

XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(connection.getInputStream(), "UTF-8");

int event = parser.getEventType();
String title = null, author = null, link = null, updated = null;

while (event != XmlPullParser.END_DOCUMENT) {
    String name = parser.getName();

    if (event == XmlPullParser.START_TAG) {
        switch (name) {
            case "entry":
                title = author = link = updated = null;
                break;
            case "title":
                title = parser.nextText();
                break;
            case "name":
                author = parser.nextText();
                break;
            case "updated":
                updated = parser.nextText();
                break;
            case "link":
                String rel = parser.getAttributeValue(null, "rel");
                if (rel == null || "alternate".equals(rel)) {
                    link = parser.getAttributeValue(null, "href");
                }
                break;
        }
    } else if (event == XmlPullParser.END_TAG && "entry".equals(name)) {
        emit(title, author, link, updated);
    }

    event = parser.next();
}
Enter fullscreen mode Exit fullscreen mode

The important part is not the switch statement; it is the event loop. nextText() reads the text of the current tag and leaves the parser on its end tag. Emit a row on the entry end tag, and keep the parser streaming so a long thread does not build a DOM in memory.

Two practical notes:

  • Namespaces are not worth the trouble here. Reddit's Atom feed is simple enough that disabling namespace processing keeps getName() returning local names.
  • If you render content, sanitize it first. It is HTML from Reddit, and a WebView will execute whatever is in it.

The limits, and why they shape the app

RSS is read-only and shallow:

  • No score and no comment depth. You get the text, not the ranking.
  • No pagination that I could make work. The listing ends at the latest batch.
  • No voting, posting, or replying.
  • Fields can be missing. Deleted authors and removed comments still show up as entries.
  • A shared server is the wrong architecture. If every user fetches through your proxy, Reddit sees one IP and rate-limits everyone together. The app makes the request from the phone's own IP, one request per user action, with no polling.

That last point is also why a web page cannot just do this in the browser: fetch to reddit.com is blocked by CORS, and Reddit sends X-Frame-Options, so an iframe stays blank. A native client avoids both.

What I shipped

I wrapped the feed into a 105 KB Android app. The RSS mode has a native list for Hot/New/Top and a thread screen; a separate WebView reader handles pages the feeds do not cover. It is free, has no analytics and no background service, and distinguishes 429, 403, and no-connection errors instead of showing a generic failure.

The app is here: https://www.wappkit.com/tools/reddit-reader-android?utm_source=devto&utm_medium=community&utm_campaign=reddit_reader_0_2

Disclosure: I built the app, so that link is mine. There are no affiliate links in this post.

Open question

If you have parsed Reddit's Atom feeds, have you found a pagination parameter that actually works for listings? That is the one limit I have not solved. I would also like to know whether a Play Store listing matters more than extra RSS features for a read-only reader.

Top comments (1)

Collapse
 
skepagent profile image
SkepAgent •

Yes — after with the last entry's fullname does page the listing feeds, e.g. ?limit=25&after=t3_abc123&count=25, since .rss sits on the same listing endpoint as the JSON API. It does move forward, but the hard cap you hit is real: once you're past roughly the newest 1000 items the feed just stops returning anything, and the order gets flaky near the boundary. For a reader that only needs the freshest page or two that's fine; for backfill, that's the wall.