
Short answer: ButterCMS offers a Feeds API that returns your blog posts as RSS or Atom, but the address it gives you contains your API token, so do not hand it to an auto-poster as it is. Serve the feed from your own domain instead. Either fetch the feed on your server and pass it on, or read the posts from the Content API and build the XML yourself. Use the publish date from the post, absolute links on your own site, escaped text, the RSS content type and a short cache, then test the live address and connect it to an auto-poster.
ButterCMS is a headless content platform. Editors write posts in a dashboard, and developers read them through an API and render them in any front end. Because the CMS does not know your site structure, a feed always involves a decision about where the final XML should live. This guide compares the two options, shows a small server route that builds the feed from the posts API, explains what to check in the output and describes how to keep the token private. Details of the API change over time, so confirm parameter names in the current reference before you copy any code.
The platform exposes feed endpoints for its blog engine. You request the feed with your token and receive an XML document that lists recent posts, in the RSS or Atom format, with filters for categories and tags. For a quick test this is the easiest path, and you can paste the response into a validator within a minute.
There are three reasons why it is not the best address to publish. First, the token sits in the query string, so anyone who sees the address can use your account for reading. A read token is less dangerous than a write token, but it is still a credential, and it would appear in every tool and log that touches the feed. Second, the links in the items must point to pages on your website. If your blog lives at a path that the CMS does not know, the links may be wrong. Third, the feed address belongs to a third party, and your readers and tools depend on it. If you change platforms, every subscriber loses the feed.
| Option | Cum funcționează | Best for |
|---|---|---|
| Pass the Butter feed through your server | Fetch the XML with the token and serve it from your domain | Quick set-up when the links are already right |
| Build the feed from the posts API | Read posts as JSON and write the XML yourself | Full control of links, dates and fields |
| Generate a static file on publish | A webhook triggers a build that writes feed.xml | Sites with a static front end |
The posts endpoint returns a list of published posts with a title, a slug, a summary, a publish date and a featured image. The route below calls it on the server, so the token never reaches the browser, and builds a clean RSS document. It uses Node 18 or later and the Express framework.
import express from "express";
import { createHash } from "node:crypto";
const SITE = "https://example.com";
const API = "https://api.buttercms.com/v2/posts/";
const ENT = { "&": "&" + "amp;", "<": "&" + "lt;", ">": "&" + "gt;", '"': "&" + "quot;", "'": "&" + "apos;" };
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ENT[c]);
async function loadPosts() {
const url = new URL(API);
url.searchParams.set("auth_token", process.env.BUTTER_TOKEN);
url.searchParams.set("page_size", "20");
const res = await fetch(url);
if (!res.ok) throw new Error("ButterCMS returned " + res.status);
const json = await res.json();
return json.data;
}
function render(posts) {
const newest = posts.length ? new Date(posts[0].published) : new Date(0);
const items = posts.map((p) => {
const link = SITE + "/blog/" + p.slug + "/";
return `<item>
<title>${esc(p.title)}</title>
<link>${esc(link)}</link>
<guid isPermaLink="true">${esc(link)}</guid>
<pubDate>${new Date(p.published).toUTCString()}</pubDate>
<description>${esc(p.summary)}</description>
</item>`;
}).join("\n");
return `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel>
<title>Example Journal</title>
<link>${SITE}</link>
<description>News and guides from Example Journal.</description>
<lastBuildDate>${newest.toUTCString()}</lastBuildDate>
${items}
</channel></rss>`;
}
let last = null;
const app = express();
app.get("/feed.xml", async (req, res) => {
try {
last = render(await loadPosts());
} catch (err) {
if (!last) return res.status(502).send("Feed temporarily unavailable");
}
const etag = '"' + createHash("sha1").update(last).digest("hex") + '"';
res.set({
"Content-Type": "application/rss+xml; charset=utf-8",
"Cache-Control": "public, max-age=300",
ETag: etag,
});
if (req.get("If-None-Match") === etag) return res.status(304).end();
res.send(last);
});
app.listen(3000);The token comes from an environment variable, so it never appears in your code or in the response. If the API fails, the route serves the last good copy, and it answers with an error only when it has nothing to serve. An empty but valid feed would be worse, because an auto-poster may read it as a sign that nothing changed. The field names above follow the posts API at the time of writing, so check them against a real response before you rely on them.
The CMS stores a slug for each post, and your front end decides the path. The example uses a blog folder and a trailing slash, but your site may differ. Build the link with the same function that your page templates use, so that the feed and the pages cannot disagree. If you ever change the path of the blog, set up redirects for the old addresses and keep the guid of existing items stable, otherwise every old article looks new. The causes of repeated posts are explained in why a feed auto-posts the same article twice.
ButterCMS lets editors choose the publish date and schedule a post for later. Use that date for the feed and never the date of the last edit. A correction to a typo should not move an article to the top of the feed. Test a scheduled post once: the item should be absent before its time and present after it, with the cache lifetime in mind. If the dashboard uses a different time zone from your server, compare the dates in the XML with the dashboard and fix any shift now, not after a post goes out at the wrong hour.
The summary field is what an auto-poster shows below the title. Make the field part of your editorial routine and keep it to one or two complete sentences. For images, the post has a featured image address, and you can add it to the feed as an enclosure with a type and a length, or leave it to the social preview of the post page. Whichever you choose, make sure the image is wide, not tiny, and that its address is public. See how to control which image gets auto-posted.
Butter lets you group posts by category and tag, and the posts endpoint accepts filters for both. Use them to offer a feed for each topic, for example one for product news and one for tutorials. Create a route for each, give it its own title and connect each address to the social channel where that audience lives. Keep a main feed that contains everything as well, because it is the address that people will try first. Check the current API reference for the exact names of the filter parameters before you use them.
Every call to the API counts against your plan, and a feed that is requested by many tools can add up. The stored copy in the example protects you, because most requests are answered from memory. If your host runs several instances, each has its own copy, so consider a shared cache or a build step that writes one file. Log failures too. A feed that returns an error for an hour at night is easy to miss, and the first sign is often that your social channels stay silent for a day.
The route sets the content type, a cache lifetime of 300 seconds and an ETag that is a hash of the document, and it answers with 304 when the client already has the current version. The mechanism is described in the guide to ETags and conditional requests. Calling the API on every request would also use your quota and slow the feed, so the cache matters for cost as well as speed. If you want updates in seconds, use a webhook from the CMS to clear the stored copy, and keep a short lifetime as a safety net. Background on delays is in why auto-posted content sometimes lags.
Request the route locally, then test the deployed address from outside, because a local test says nothing about your hosting platform or your certificate.
curl -i https://example.com/feed.xml
curl -i -H 'If-None-Match: "paste-the-etag-here"' https://example.com/feed.xmlA longer routine is in the feed validation guide, and a shorter one in debugging a feed with curl.
PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your feed, connect your accounts and choose how each post is built from the title, the description or the page title. PostRSS checks the feed as often as every minute and shares each new entry, with its image and link, to the channels you choose among 66 supported networks, messengers, team chats and blogs, including LinkedIn, X, Bluesky, Mastodon and Telegram. Keyword filters, UTM parameters and posting windows let you tune each channel. See the PostRSS features page and the plan limits on the pricing page. More developer guides are under the Technical Guide tag.
ButterCMS gives you the pieces for a feed, and your job is to put them on your own domain. Keep the token on the server, build links with the same logic as your pages, use the publish date that editors set, write real summaries, escape the text, send the right headers and cache the result. Test the live address from outside and then connect it to an auto-poster, so that every post your editors publish reaches your readers and your social channels without extra work.
It offers a Feeds API for blog posts, so you can fetch RSS or Atom from the platform. For public use, serve the result from your own domain so that your token stays private.
The address contains your API token and belongs to a third party. Anyone who sees it can use the token, and a change of platform would break the feed.
The publish date that your editors set in the dashboard. Do not use the edit date, because a correction would move the article to the top.
Use a webhook from the CMS to clear the stored copy, and keep a short cache lifetime as a safety net in case a webhook fails.
Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login. Paste the address, connect your channels and new posts are shared as soon as they appear.
What changed in the networks, what broke, and how to fix it before it costs you reach.
Create de Internet Solutions, echipa din spatele PostRSS. Fiecare produs vă economisește timp în felul său.