No description
Find a file
2026-10-08 17:45:11 +02:00
cmd/headlines initial release 2026-10-08 17:45:11 +02:00
testdata initial release 2026-10-08 17:45:11 +02:00
.gitignore initial release 2026-10-08 17:45:11 +02:00
go.mod initial release 2026-10-08 17:45:11 +02:00
go.sum initial release 2026-10-08 17:45:11 +02:00
headlines.go initial release 2026-10-08 17:45:11 +02:00
headlines_test.go initial release 2026-10-08 17:45:11 +02:00
live_test.go initial release 2026-10-08 17:45:11 +02:00
README.md initial release 2026-10-08 17:45:11 +02:00
source.go initial release 2026-10-08 17:45:11 +02:00
source_test.go initial release 2026-10-08 17:45:11 +02:00
sources.go initial release 2026-10-08 17:45:11 +02:00

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.