Sources
A source is one feed of content: an RSS feed, a subreddit, an arXiv
search, and so on. Each source has a source_type and a config object
whose shape depends on the type. This page documents every supported type.
All sources share two common fields regardless of type:
Field |
Default |
Notes |
|---|---|---|
|
|
Minimum |
|
|
Inactive sources are skipped by the fetch scheduler. |
Sources with an identical (source_type, config) pair across different
users are fetched once per cycle and fanned out to every subscriber — see
Architecture.
RSS / Atom
{"url": "https://example.com/feed.xml"}
url is the only field. If it points at an HTML page rather than a raw
feed, Shoebill tries to autodiscover a feed link on that page. atom is
handled by the same fetcher as rss — there’s no configuration
difference between the two types.
Reddit
{
"subreddit": "MachineLearning",
"sort": "hot",
"limit": "25"
}
Field |
Required |
Default |
Notes |
|---|---|---|---|
|
yes |
— |
No |
|
no |
|
|
|
no |
|
|
|
no |
— |
Per-source Reddit API credentials. Falls back to the global |
|
no |
— |
Only needed for the OAuth “password” grant (script-type Reddit apps); without them, Shoebill authenticates as |
Adding a Reddit source in the UI auto-fills the credential fields from any Reddit source you’ve already configured, so you only enter your API credentials once even when adding several subreddits.
Mastodon
{"instance_url": "mastodon.social", "feed_type": "hashtag", "name": "opensource"}
Field |
Required |
Default |
Notes |
|---|---|---|---|
|
yes |
— |
Scheme is added automatically if omitted |
|
no |
|
|
|
required unless |
— |
Account name (for |
Uses each instance’s built-in RSS endpoints (/@user.rss, /tags/x.rss,
/public/local.rss) rather than the Mastodon API — no auth needed.
Bluesky
{"feed_type": "user", "handle": "bsky.app", "limit": "20"}
Field |
Required |
Default |
Notes |
|---|---|---|---|
|
no |
|
|
|
required for |
— |
Leading |
|
required for |
— |
|
|
no |
|
Capped at 100 |
Uses the public AT Protocol AppView API — no authentication required.
Lemmy
{"instance_url": "lemmy.world", "community": "technology", "sort": "Hot", "limit": "25"}
Field |
Required |
Default |
|---|---|---|
|
yes |
— |
|
yes |
— |
|
no |
|
|
no |
|
GitHub
Two modes, chosen via mode:
{"mode": "releases", "repo": "openai/openai-python", "limit": "20"}
{"mode": "trending", "language": "python", "since": "daily"}
Field |
Required |
Default |
Notes |
|---|---|---|---|
|
no |
|
|
|
required for |
— |
|
|
no |
|
Capped at 100 |
|
no |
— |
A GitHub PAT, for higher API rate limits |
|
no |
|
Include prereleases; |
|
no |
all languages |
For |
|
no |
|
For |
arXiv
{"query": "transformer architecture"}
query is the only field — searches arXiv’s official Atom API directly
(not Google Scholar, despite this fetcher historically also being
reachable under a "scholar" type name — that legacy alias has since
been removed; any existing sources that used it were migrated to
"arxiv" automatically).
Telegram
{"channel": "durov"}
channel (no @ prefix) is the only field. Scrapes the public
t.me/s/{channel} web preview; channels without a public preview fail
gracefully (empty result, logged warning) rather than erroring.
Generic scraper
For sites with no feed at all:
{
"url": "https://example.com/news",
"item_selector": "article.post",
"title_selector": "h2",
"link_selector": "a",
"content_selector": ".post-body",
"fetch_full_articles": "true"
}
Field |
Required |
Default |
Notes |
|---|---|---|---|
|
yes |
— |
|
|
yes |
— |
CSS selector matching each article’s container element |
|
no |
|
|
|
no |
|
|
|
no |
— |
Falls back to the full container’s text if omitted |
|
no |
= |
For resolving relative links |
|
no |
|
Follow each article’s link to fetch the full text when the listing excerpt is under 300 characters |
robots.txt is checked both when the source is created and on every
subsequent fetch — a scraper source can’t be created against a site whose
robots.txt disallows it.
Auto-detect selectors: rather than writing CSS selectors by hand, the
“Auto-detect” button in the source form calls the configured LLM to
propose them — it fetches the page, strips it to a simplified
tag/class/href skeleton, asks the LLM for item_selector /
title_selector / link_selector / content_selector, validates the
result by actually running the selectors against the page, and retries
once if the first attempt matches zero items.