mmcdole/gofeed: Parsing RSS, Atom and JSON Feeds in Go
Parse RSS, Atom and JSON feeds in Go
At a glance
- What is it?
- gofeed is an MIT-licensed Go library that turns RSS, Atom and JSON feeds into one unified Feed struct, with best-effort recovery from malformed XML. It suits Go services that ingest many third-party feeds; it is not a crawler, a scheduler or a feed reader.
- Who is it for?
- Use mmcdole/gofeed if you are writing a Go service that has to read feeds from sources you do not control, because the unified Feed model and the tolerant XML handling remove most of the per-publisher special cases. Do not use it as a feed reader, a scheduler or a crawler: it parses documents you hand it and nothing more, and the README does not document rollback or a migration path between releases.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What gofeed solves for Go services that read other people's feeds
Every feed-consuming service eventually hits the same wall. RSS 0.90, RSS 2.0, Atom 0.3, Atom 1.0 and JSON Feed 1.0/1.1 all describe roughly the same thing with different element names, and publishers break the spec in ways that vary by publisher. Writing a decoder per format means maintaining four mappings and a pile of exceptions.
gofeed targets that gap. The README describes it as a library for parsing RSS, Atom and JSON feeds that "effectively manages non-standard elements and known extensions" and takes a best-effort approach to broken or invalid XML: unescaped markup, undeclared namespace prefixes, missing or illegal tags, incorrect date formats. The audience is a Go backend that ingests feeds it does not control, where one malformed document should not stop the batch. It is not aimed at someone who wants to read feeds; there is no UI, no scheduler and no storage in the repository layout.
The unified Feed model, translators and the two parser paths
The design has two layers. Format-specific parsers (rss.Parser, atom.Parser, json.Parser) map a document to structs that match that format's own names, so an RSS consumer sees rss.Feed with fields like WebMaster. Above them sits the universal gofeed.Parser, which detects the format and runs a translator to produce one gofeed.Feed.
The translators are the interesting part. DefaultRSSTranslator, DefaultAtomTranslator and DefaultJSONTranslator convert each format into the shared model, and the README says you can implement your own gofeed.Translator. The worked example is a custom translator that gives /rss/channel/itunes:author precedence over /rss/channel/managingEditor when populating Feed.Author. That matters because field precedence differs between sources, and the default ordering will not fit every pipeline.
Extensions follow a separate path. Elements outside the feed's default namespace are stored as tree-like structures under Feed.Extensions and Item.Extensions, so custom elements stay reachable without a schema change. Dublin Core and Apple iTunes get native structs (Feed.DublinCoreExt, Item.DublinCoreExt, Feed.ITunesExt, Item.ITunesExt) rather than living in the generic tree. The trade-off is visible in the layout: anything not in that short built-in list arrives as an untyped tree you walk yourself.
Installing gofeed and parsing a feed from a URL
The module path is github.com/mmcdole/gofeed and go.mod declares go 1.25.0, so a toolchain at that version or newer is required. Add it to your module in the usual way.
The README's first example builds a parser and reads a feed over HTTP. ParseURL returns a *gofeed.Feed and an error; the example discards the error with _, which you should not copy in production code.
fp := gofeed.NewParser()
feed, _ := fp.ParseURL("http://feeds.twit.tv/twit.xml")
fmt.Println(feed.Title)If the source needs a timeout, the README shows ParseURLWithContext with a context. This is the form to use when you are pulling many feeds and cannot let one slow host hold a worker.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
fp := gofeed.NewParser()
feed, _ := fp.ParseURLWithContext("http://feeds.twit.tv/twit.xml", ctx)
fmt.Println(feed.Title)For sources behind HTTP basic auth, the parser takes an AuthConfig field. The README gives the struct with Username and Password keys, and a UserAgent field for a custom agent string.
fp := gofeed.NewParser()
fp.AuthConfig = &gofeed.Auth{
Username: "foo",
Password: "bar",
}When the bytes are already in hand (a cache, an object store, a test fixture), ParseString and Parse take the document directly and skip the network entirely. The README shows ParseString with an inline RSS document and Parse with an os.Open file handle.
Best-effort parsing is a recovery strategy, not a validation guarantee
The tolerance for unescaped markup, undeclared namespace prefixes and illegal tags is the feature most likely to decide adoption, and it is also the one to think hardest about. Best-effort means the parser tries to produce a Feed from input a strict decoder would reject. That is the right default when your source list is long and you cannot fix publishers.
It also means a document that is wrong in a way the parser cannot repair may still come back with fields silently empty rather than with an error you can route to a dead-letter queue. The README does not document which malformed inputs are recovered and which are not, so the recovery envelope is something you have to measure against your own sources.
A second boundary is scope. gofeed parses a document. It does not poll, cache, deduplicate entries, follow pagination or store state; those live in your service. If you were hoping for a feed reader, this is the wrong layer. The repository does include a cmd/ directory, but the README documents the library API, not a command-line tool, so do not assume a ready-made binary.
How gofeed compares with Python's feedparser
feedparser is the obvious reference point, and it appears in the searches around this project. The two solve the same problem in different runtimes: feedparser is a Python library that normalizes RSS and Atom into one dictionary-like result, and gofeed is a Go library that normalizes RSS, Atom and JSON Feed into one struct.
The practical differences follow from that. If your ingestion service is already Go, gofeed keeps the whole path in one binary and one dependency graph, which go.mod keeps small: golang.org/x/net, golang.org/x/text, github.com/mmcdole/goxpp/v2 and testify for tests. If your pipeline is Python, feedparser is the shorter path and there is no reason to bridge runtimes for a parser.
JSON Feed is the other split. gofeed lists JSON 1.0 and 1.1 as first-class formats alongside RSS and Atom, with its own translator, rather than as an add-on. That matters if your source list already mixes JSON Feed endpoints with XML ones.
Maintenance, upgrades and what the MIT licence leaves you
The repository is not archived and the last push was on 2026-09-14. Recent tagged releases are v1.4.0 on 2026-07-11, v1.4.1 on 2026-08-10 and v1.4.2 on 2026-08-20, so the project is still cutting releases on a roughly monthly cadence.
Upgrade cost is mostly a function of how much of the surface you touch. If you call gofeed.NewParser and read Feed.Title, a minor bump is close to free. If you wrote a custom Translator against the rss.Feed or atom.Feed structs, a change to those structs is a compile error in your code, which is inconvenient but loud. The README does not document rollback or a migration path between releases, so pin a version in go.mod and read the release notes before moving.
The licence is MIT. That is permissive: it allows commercial and closed-source use, and it requires that the copyright notice and permission notice be included in copies or substantial portions of the software. This is a description of the licence text, not legal advice; check your own distribution obligations.
Editorial conclusion
Use mmcdole/gofeed if you are writing a Go service that has to read feeds from sources you do not control, because the unified Feed model and the tolerant XML handling remove most of the per-publisher special cases. Do not use it as a feed reader, a scheduler or a crawler: it parses documents you hand it and nothing more, and the README does not document rollback or a migration path between releases. Before you commit, verify two things yourself: how the parser behaves on the worst feed in your own source list, and whether the fields you depend on survive the translator for each of the three formats. The library is MIT licensed and the last push was on 2026-09-14.
Frequently asked questions
How do I install mmcdole/gofeed?
It is a Go module and the go.mod in the repository declares go 1.25.0, so you need a toolchain at that version or newer. Add github.com/mmcdole/gofeed to your module's dependencies as you would any other Go package.
Which feed formats does mmcdole/gofeed support?
The README lists RSS from 0.90 to 2.0, Atom 0.3 and 1.0, and JSON Feed 1.0 and 1.1. Each format has its own parser, and the universal parser translates all of them into a single gofeed.Feed.
Can I parse a feed from a string instead of a URL in mmcdole/gofeed?
Yes. The README shows ParseString for a document already in memory and Parse for an io.Reader such as an open file, both on a parser created with gofeed.NewParser. ParseURL and ParseURLWithContext are the network paths.
Does mmcdole/gofeed handle broken or invalid XML feeds?
The README states that it takes a best-effort approach to broken or invalid XML, listing unescaped markup, undeclared namespace prefixes, missing or illegal tags and incorrect date formats. It does not document which malformed inputs are recovered and which are not.
What does RSS feed stand for?
The README only lists RSS versions 0.90 to 2.0 as formats that gofeed parses; it does not define the acronym. The expansion of the name is outside what the repository documents.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mmcdole-gofeed)