Skip to content
Docs

Read the feed

The feed is how a site that shows its articles itself gets them. It is static JSON, served from feeds.citedseo.ai through Cloudflare’s network, so reading it is fast and you can read it as often as you like.

It is not an API you query. It is a snapshot of exactly the articles that are live on your site, written again each time one is published, changed or taken down, and when the site’s article settings or an author change.

Everything for one site lives under its site ID:

feeds.citedseo.ai/{siteId}/
feed/index.json the manifest
b/{buildId}/page-1.json 20 articles a page, newest first
b/{buildId}/page-2.json
b/{buildId}/by-slug/{slug}.json one article

There is no key, no header and no query parameter on any of them. Any origin may read them, so they work from the browser too.

  1. Read the manifest. It is the only file that changes in place, and it names the current build.

  2. Read from that build. Its pages for the whole list, or one by-slug file for a single article.

Always start from the manifest. Never keep a buildId in your code: it changes with every publish.

GEThttps://feeds.citedseo.ai/{siteId}/feed/index.json
buildIdstring

The current build. Use it in the address of every other file.

totalnumber

How many articles are live.

pageCountnumber

How many pages the build has. 0 when nothing is live.

pageSizenumber

How many articles a full page holds: 20.

latestModifiedAtstring | null

When the newest article in the build last changed, as an ISO 8601 time in UTC. null when nothing is live.

generatedAtstring

When this build was written.

removedtrue

Present only on the feed of a site that has been removed from CitedSEO: the manifest stays, with total 0, and nothing more is published to it.

200 OK
{
"buildId": "01JA2M7Q4K8ZP3",
"total": 42,
"pageCount": 3,
"pageSize": 20,
"latestModifiedAt": "2026-10-11T02:05:00.000Z",
"generatedAt": "2026-10-11T02:05:04.000Z"
}
GEThttps://feeds.citedseo.ai/{siteId}/b/{buildId}/page-{n}.json

Pages are numbered from 1 to the manifest’s pageCount. Each holds up to 20 articles, newest first.

articlesArticle[]

The page’s articles, whole: every field, with both bodies.

200 OK
{
"articles": [
{
"id": "0b6f6c1e-3f0a-4a59-9d0e-2f8f4c1f7a31",
"slug": "how-to-brew-green-tea",
"url": "https://example.com/blog/how-to-brew-green-tea",
"title": "How to brew green tea",
"description": "Brew green tea at 80°C for two minutes. Here is why the temp…",
"cover": {
"src": "https://example.com/images/green-tea.webp",
"alt": "A glass teapot of green tea"
},
"author": {
"name": "Sam Lee",
"url": "https://example.com/authors/sam-lee"
},
"publishedAt": "2026-10-09T02:05:00.000Z",
"modifiedAt": "2026-10-11T02:05:00.000Z",
"html": "<p class=\"citedseo-answer\"><strong>Brew green tea at 80°C for two minutes. Hot…",
"markdown": "**Brew green tea at 80°C for two minutes. Hotter water makes…",
"structuredData": {
"@context": "https://schema.org",
"@graph": [
"…"
]
}
}
]
}

The long fields are cut short above.

GEThttps://feeds.citedseo.ai/{siteId}/b/{buildId}/by-slug/{slug}.json

Put the slug in the address as the article’s slug has it. That is already URL-encoded (caf%C3%A9, not café), so don’t encode it again; encode a slug only when it reached you decoded, as most routers give it. The file answers 404 when no live article has that slug, which is how you tell a page that doesn’t exist.

articleArticle

The article, the same object a page holds.

200 OK
{
"article": {
"id": "0b6f6c1e-3f0a-4a59-9d0e-2f8f4c1f7a31",
"slug": "how-to-brew-green-tea",
"url": "https://example.com/blog/how-to-brew-green-tea",
"title": "How to brew green tea",
"description": "Brew green tea at 80°C for two minutes. Here is why the temp…",
"cover": {
"src": "https://example.com/images/green-tea.webp",
"alt": "A glass teapot of green tea"
},
"author": {
"name": "Sam Lee",
"url": "https://example.com/authors/sam-lee"
},
"publishedAt": "2026-10-09T02:05:00.000Z",
"modifiedAt": "2026-10-11T02:05:00.000Z",
"html": "<p class=\"citedseo-answer\"><strong>Brew green tea at 80°C for two minutes. Hot…",
"markdown": "**Brew green tea at 80°C for two minutes. Hotter water makes…",
"structuredData": {
"@context": "https://schema.org",
"@graph": [
"…"
]
}
}
}
const feed = 'https://feeds.citedseo.ai/YOUR_SITE_ID';
const manifest = await fetch(`${feed}/feed/index.json`).then((res) => res.json());
// slug as the article has it, already URL-encoded.
const res = await fetch(`${feed}/b/${manifest.buildId}/by-slug/${slug}.json`);
if (!res.ok && res.status !== 404) throw new Error(`The feed answered ${res.status}`);
// 404 is the one answer that means there is no such article.
const article = res.ok ? (await res.json()).article : null;
  • Only what is live. An article appears once it has been published, at the version that is live. A newer draft waiting for approval is not in the feed until it ships.
  • Every live article, wherever it publishes. A site connected to WordPress has the same articles in its feed as on WordPress.
  • Nothing private. The feed is what your site already shows the public.