- Go 100%
| cmd/headlines | ||
| testdata | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| headlines.go | ||
| headlines_test.go | ||
| live_test.go | ||
| README.md | ||
| source.go | ||
| source_test.go | ||
| sources.go | ||
headlines
Go library that aggregates news from built-in RSS and Atom sources. One call gets you recent items from every source, tagged with source, language and category, ready to print for people or to hand to agents and tools as JSON.
items, err := headlines.Fetch(ctx)
Library
Options narrow the selection before anything is downloaded:
items, err := headlines.Fetch(ctx,
headlines.WithLanguages("es"), // "pt" also matches "pt-BR"
headlines.WithCategories(headlines.Tech), // general, tech, business, science
headlines.WithSources("euronews", "xataka"),
headlines.WithMaxAge(24*time.Hour),
headlines.WithPerFeedLimit(10), // default 20, <= 0 for all
)
Items come back newest first. An item a source lists in more than one of its feeds shows up once, under the most specific category.
Fetch returns whatever it could get. If some feeds fail, err joins one *headlines.FeedError per failed feed and items still holds the rest, so only treat the error as fatal when there are no items:
items, err := headlines.Fetch(ctx)
if err != nil && len(items) == 0 {
return err
}
An Item serializes like this:
{
"id": "https://www.federalreserve.gov/newsevents/pressreleases/monetary20261007a.htm",
"title": "Minutes of the Federal Open Market Committee, September 15-16, 2026",
"url": "https://www.federalreserve.gov/newsevents/pressreleases/monetary20261007a.htm",
"summary": "Plain text, a few hundred characters at most. Omitted when the feed has none.",
"published": "2026-10-07T18:00:00Z",
"source": "fed",
"language": "en",
"category": "business"
}
headlines.Sources() lists the built-in sources with their feeds, languages and categories.
CLI
go install code.rbel.co/rubiojr/headlines/cmd/headlines@latest
headlines # everything, grouped by category
headlines -l es -c tech --summary # Spanish tech news, with summaries
headlines -s democracynow -n 10 # latest 10 Democracy Now! headlines
headlines --max-age 6h --json # JSON array, for tools and agents
headlines sources # what's built in
headlines sources --json # same, with every feed URL
Failed feeds are reported as warnings on stderr. The command only fails when nothing could be fetched.
Sources
| Source | Languages | Categories |
|---|---|---|
| democracynow | en | general |
| bbc | en, es, pt-BR | general, tech, business |
| dw | en, es, de | general, business, science |
| france24 | en, es, fr | general, business |
| euronews | en, es, fr, de, it, pt | general, tech, business |
| elpais | es, en | general, tech, business |
| hackernews, theverge, arstechnica | en | tech |
| xataka | es | tech |
| heise | de | tech |
| investing | en, es, de, fr, it, pt-BR | business |
| cnbc | en | business |
| expansion | es | business |
| ecb, fed | en | business |
Democracy Now!'s feed bundles each day's headlines into a single entry, so the library splits it into one item per headline. Per-headline summaries only exist on the website, not in the feed, so those items have none.
Adding one
Add it to sources.go. A few things learned the hard way:
- Declare the language and category for every feed. Many feeds don't declare a language, and some declare the wrong one (The Conversation's Spanish edition says
en-US). - Use the final URL. Some section feeds silently redirect to the front page, which would put every item under the wrong category.
- Run the live test, which fails on redirects and on feeds whose newest item is more than two weeks old:
go test -tags live -run TestLiveFeeds .
Tests
go test ./...
Unit tests run against local fixtures (RSS 2.0, RSS 1.0/RDF, Atom and a trimmed Democracy Now! feed) and don't touch the network.