When to use this
Use the Public Blog API when:- Your site runs on Cloudflare Pages, Vercel, Netlify, or GitHub Pages
- You don’t use a CMS like WordPress, Ghost, or Webflow
- Your site is built with a static site generator (Astro, Next.js, Nuxt, Hugo, Gatsby, 11ty, etc.)
- You want full control over how blog content is rendered on your site
How it works
theStacc generates and stores your blog content. The Public Blog API exposes the published posts for one project as JSON. Your static site fetches them at build time and generates static HTML pages.Authentication
Every project gets one Public API Key for read-only public access. Find it in your project under Settings > Publishing > API Access. Click Generate API Key if you don’t have one yet; Reveal and Copy to read it. The key always starts withpk_live_. It is project-scoped: it only ever returns the published blogs that belong to that one project.
Include the key in every request — either as a query parameter or as a Bearer header:
Authorization: Bearer header wins.
This is a project key, not an agent key
Thepk_live_ Public API Key is read-only. It can list and read your published blogs — nothing more. It cannot create, edit, publish, or delete content, and it can’t see drafts or unpublished posts. That’s why it’s safe to use in a static-site build.
It is a completely different credential from the agent keys (which start with sk_agent_) used by the Model Context Protocol (MCP) server. Agent keys are secret, write-capable (they can generate, edit, and publish blogs), are stored hashed, and live in the Agent access section right beside API Access. Never use one in place of the other — and never embed an sk_agent_ key in a public build.
Keep your key safe
Though the Public API Key is read-only, treat it as a credential. Store it in an environment variable in your build process and never commit it to your repository. If a key is ever exposed, open Settings > Publishing > API Access and click Rotate Key to issue a new one (the old key stops working immediately), or Revoke to disable public access entirely. Any site still using the old key will stop fetching the moment you rotate or revoke, so update your build environment variable at the same time.Endpoints
All endpoints share the base URLhttps://api.thestacc.com/blog/api/v1/public.
List published blogs
status: published, sorted by published_at (newest first). By default it returns metadata only (no body content) for a fast, lightweight listing. Pass include_content=true to get the full HTML body for every post in the page.
Query parameters:
api_key(required) — your project’spk_live_key (or pass it via the Authorization header instead)limit(optional) — number of blogs to return. Default50, minimum1, maximum100offset(optional) — pagination offset. Default0, minimum0category(optional) — return only blogs that include this category nameinclude_content(optional) — set totrueto include the full HTML body of each blog. Defaultfalse
limit and offset together to page through more than 100 posts. The total field in the response is the full count of published blogs (after any category filter), so you can compute how many pages remain.
Response (default — without content):
include_content=true is passed, each blog also includes a content field with the full HTML body.
Get a single blog by slug
updated_at and word_count. word_count is computed on the fly by stripping the HTML tags from the body and counting the remaining words, so it always reflects the current content.
Response:
404 if the slug doesn’t exist in this project or the matching blog isn’t published.
Get blog sitemap data
slug, published_at, and updated_at — just enough to build <loc> and <lastmod> entries.
This endpoint returns up to 5,000 blogs in a single response (newest first), with no pagination. If a project ever publishes more than 5,000 posts, the sitemap call returns the most recent 5,000. The total field is the number of blogs actually returned.
Response:
Content format
Thecontent field is HTML — the same body HTML theStacc generates for your post. It includes:
- Headings (
h2,h3,h4) - Paragraphs, lists, and blockquotes
- Links (internal and external)
- Images with alt text
- Inline styles for formatting
set:html (Astro), dangerouslySetInnerHTML (React/Next.js), or | safe (Hugo). Apply your own CSS to match your site’s design.
The featured image is separate from the body
featured_image_url is a standalone field, not part of the content HTML. theStacc treats your post’s hero image (or, if there’s no dedicated hero, the first generated image) as a structured field so you can render it however you like — as a hero banner above the article, as a card thumbnail in your listing, or in your social/OG tags.
The body content is the article itself and is not prefixed with the featured image, so the hero won’t appear twice. In your template, render featured_image_url as the hero and render content as the body. Any other images that belong inside the article stay inside content as normal <img> tags.
Cross-post links are resolved for you
When theStacc writes a post that links to another one of your posts, it stores that link as an internal placeholder ([INTERNAL:other-post-slug]) rather than a hard URL, so the link survives no matter where you publish. The Public API resolves these placeholders for you on every read whenever it returns body content (the single-blog endpoint, and the list endpoint when include_content=true):
- If the referenced slug matches another published post in the same project, the placeholder becomes a real link to that post on your site, using the path convention
your-site.com/blog/<slug>/. The base URL comes from the website connected to your project; if no site URL is set, the link is rendered as a root-relative/blog/<slug>/path. - If the referenced post isn’t published yet (or the slug doesn’t match anything), the link is dropped but the anchor text is kept. You’ll never receive a broken link or a raw
[INTERNAL:...]placeholder.
your-site.com/blog/<slug>/ URL pattern used throughout the Static Site Integration guide. If your blog lives at a different path, you’ll want to keep that path as /blog/ (or adjust your routing accordingly) so resolved internal links point to the right place.
Self-hosting images
Images referenced incontent and featured_image_url are served from theStacc storage. For best performance and to avoid depending on our storage, many teams download these images during their build and rewrite the URLs to their own CDN. This is optional — the URLs work as-is.
Rate limits
The Public Blog API is designed for build-time fetching, not real-time client requests. A typical static site build makes a handful of requests (a list or sitemap call, plus one per post). Fetch the content during your build, generate static pages, and serve those — don’t call the API from client-side browser code on every page view. If you have a very large catalog, page through the list endpoint withlimit/offset rather than requesting everything at once, and use the lightweight sitemap endpoint (which returns just slugs and dates) when you only need URLs.
Error handling
All errors return a JSON object with adetail field describing what went wrong:
401— Missing or invalid API key. The key wasn’t supplied, or it doesn’t match any project (often because it was rotated or revoked). Check the value in your build environment.404— Blog not found or not published. The slug doesn’t exist in this project, or the matching post isn’t published. (Only the single-blog endpoint returns this.)500— Server error. Transient — retry after a few seconds.
200 response as a failure: throw on the list/sitemap calls so a broken build is obvious, and return null on the single-blog call so you can render a 404 page for a missing slug.
Next steps
- Follow Static Site Integration for framework-specific setup (Astro, Next.js, Hugo, and more)
- Set up Deploy Hooks so your site rebuilds automatically when new content is published
- Want an AI assistant or automation to create and publish posts on your behalf? That’s a different, write-capable credential — see Agent Keys & MCP
