Skip to content
Docs

JavaScript and TypeScript

@citedseo/feed is the feed and the webhook check as a package: a few functions over fetch, with no dependencies and no key. It runs in Node.js 20+, Cloudflare Workers, Deno, Bun and the browser, and it is what each framework package is built on.

Terminal window
npm install @citedseo/feed
import { fetchArticle, fetchArticles } from '@citedseo/feed';
const articles = await fetchArticles({ siteId: 'YOUR_SITE_ID' });
const article = await fetchArticle({ siteId: 'YOUR_SITE_ID', slug: 'how-to-brew-green-tea' });
Function Gives What it reads
fetchArticles(options) FeedArticle[], newest first The manifest, then every page of the build it names.
fetchArticle(options) FeedArticle | null The manifest, then one small file. null when no article has that slug.
fetchManifest(options) FeedManifest The manifest alone, to see whether anything changed.
jsonLd(article) string | null Nothing. It gives the article’s structured data as text that is safe inside a script tag.
/** What every read takes. */
export type FeedOptions = {
/** Your site ID. It is the only thing the feed needs. */
siteId: string
/** Another origin to read from, for tests. Defaults to https://feeds.citedseo.ai. */
origin?: string
/** Your own fetch: a framework's caching one, or a test's. Defaults to the global fetch. */
fetch?: typeof fetch
}

Pass fetch to read through a cache of your own, such as SvelteKit’s fetch in a load function.

Every function is typed, and the types are exported. They are the ones CitedSEO’s own engine builds the feed against, so they can’t fall behind it.

import type { FeedArticle, FeedManifest, WebhookEvent, WebhookPost } from '@citedseo/feed';

The article prints them in full.

import { webhookHandler } from '@citedseo/feed';
export const POST = webhookHandler({
secret: process.env.CITEDSEO_WEBHOOK_SECRET!,
onPost: async (post) => {
// post.event is "article.published" or "article.updated"; post.data is the article.
},
});
Function What it does
webhookHandler({ secret, onPost }) A whole route: answers 400 to a post that isn’t ours, runs onPost for one that is, answers 204, or 500 when onPost throws so the post is tried again.
readWebhook(request, secret) The verified post as a WebhookPost, or null.
verifyWebhook(secret, body, header) Whether a signature header is ours for a raw body.

See Verify the signature for what the check does.

A read that fails throws a CitedSEOError with a code to branch on:

code When
CONFIG The site ID or slug is missing.
NOT_FOUND There is no feed for that site ID.
NETWORK_ERROR The feed couldn’t be reached.
SERVER_ERROR The feed answered with an error; status has the code.
INVALID_RESPONSE A file wasn’t the shape it should be.

An article that isn’t there is not an error: fetchArticle gives null.

Each of these re-exports everything above, so one import covers a site.