Skip to main content

Sources and ingestion

A source is a feed the dashboard polls. Ingestion is the scheduled job that fetches every enabled source and creates articles.

Managing sources

RouteMethodPurpose
/api/sourcesGETList configured sources.
/api/sourcesPOSTAdd a source.
/api/sources/{slug}DELETERemove a source.
/api/sources/{slug}/enabledPATCHEnable or disable without deleting.
/api/sources/{slug}/priorityPATCHChange fetch priority.

Sources are addressed by slug, not numeric ID, so URLs stay readable and stable across environments.

Disabling is preferable to deleting when you only want to pause a feed — delete discards the source's configuration, while disable leaves it in place and simply skips it during ingestion.

Previewing before you commit

POST /api/sources/preview
POST /api/sources/substack/preview

Both fetch a candidate feed and return a sample of what would be ingested, without persisting anything. Use them to validate a URL in the UI before creating the source.

Substack has its own preview route because Substack publications can be served from custom domains, and resolving the real feed URL takes provider-specific logic rather than a generic RSS probe.

Source health

GET /api/sources/health

Reports per-source fetch health so broken feeds surface before they silently stop producing articles.

Two related endpoints help prune dead feeds:

RouteMethodPurpose
/api/sources/cleanup-suggestionsGETSources that look dead or duplicated.
/api/sources/cleanupPOSTApply a cleanup selection.

OPML import and export

RouteMethodPurpose
/api/sources/export.opmlGETExport all sources as OPML.
/api/sources/importPOSTImport an OPML file.

Import is bounded at 5 MiB and 1000 outlines. Larger uploads are rejected rather than streamed, which caps per-request memory and time for oversized or hostile files. OPML is plain text and real subscription lists sit far below this ceiling.

Running ingestion

RouteMethodPurpose
/api/ingestPOSTTrigger an ingestion run now.
/api/ingest/streamGETStream progress of a running ingestion.
/api/ingest/runsGETHistory of ingestion runs.
/api/ingest/runs/{run_id}GETDetail for one run.

/api/ingest/stream is a streaming response intended for a live progress view, not a polling endpoint — open it once and read events as they arrive.

In Kubernetes deployments, scheduled ingestion runs as a separate CronJob workload rather than inside the web process. Triggering /api/ingest runs it in-process instead, which is convenient for testing but competes with request serving on a small instance.

Scheduler

RouteMethodPurpose
/api/scheduler/statusGETCurrent scheduler state and next run.
/api/scheduler/intervalPOSTChange the ingestion interval.
/api/scheduler/pausePOSTPause scheduled ingestion.
/api/scheduler/resumePOSTResume it.
/api/scheduler/job-runsGETHistory across all scheduled jobs.
/api/scheduler/jobs/embedding-dedup/runPOSTRun embedding de-duplication now.

Pausing the scheduler stops automatic runs but leaves manual /api/ingest available.

Watchlists

Watchlists are standing interest definitions evaluated against newly ingested articles.

RouteMethodPurpose
/api/watchlistsGETList watchlists.
/api/watchlistsPOSTCreate one.
/api/watchlists/{watchlist_id}PATCHUpdate one.
/api/watchlists/{watchlist_id}DELETEDelete one.
/api/watchlists/previewPOSTPreview matches before saving.
/api/watchlists/nudgesGETSuggestions derived from watchlist activity.

As with sources, preview lets you check a definition against existing articles before committing it.