Skip to content

Keyless reads

Reading your published content from Ghost Writr needs no API key. The site ID is the read capability, and every SDK is built around that single fact.

Your siteId is an unguessable UUIDv7. Knowing it is sufficient to read the site’s feed, and because the id space can’t be enumerated, no one can discover or read another site’s content by guessing. There is one identifier to share and nothing extra to provision.

import { fetchArticles } from "@ghostwritr/feed";
// The siteId is the only credential. No key, no token, no header.
const articles = await fetchArticles({ siteId });

Every framework SDK reduces to the same configuration. @ghostwritr/next is createGhostwritr({ siteId }); @ghostwritr/astro is ghostwritr({ siteId }); @ghostwritr/react-router and @ghostwritr/feed take { siteId } on each fetch. None of them accept — or want — an API key for reads.

The only read path is the public static feed at feeds.ghostwritr.io/{siteId}/. The keyless SDKs do not fall back to a dynamic authenticated endpoint when a file is missing. They fail closed: a missing feed throws a GhostwritrError with code NOT_FOUND rather than reaching for a privileged path.

Because the read client carries no secret, it is safe wherever your code runs — server components, static export, edge functions, and the browser. There is no key to leak in a bundle, no env var to guard, no origin restriction to configure. The worst case for a leaked site ID is that someone reads content you already published publicly.

This is what makes <ArticleContent> and the keyless fetchers usable in client components and at build time without ceremony.

Publishing is not part of the SDK surface, and you do not need it to render a blog.

ReadWrite
CredentialsiteId (public, unguessable)a separate write key
Wherestatic feed on feeds.ghostwritr.iothe authoring/engine side
In the SDKsyes — every read goes through itno — SDKs are read-only
Safe to ship to the clientyesno

The write key lives entirely on the authoring side and is write-only — it never appears in a read SDK, a page, or a bundle. If you are wiring up a frontend, you only ever touch the read path, and the read path is keyless.