Using Ghost's Content API for a Headless Frontend: A Practical Walkthrough
Learn how to fetch posts from Ghost's Content API, integrate them with a static site generator, and understand the trade‑offs of going headless.
21 Feb 2026, 13:44 UTC

The Problem: Theme‑Bound Rendering Limits Performance
When a Ghost site relies on the built‑in Handlebars theme, every page request is rendered on the server. This works fine for modest traffic, but as you aim for sub‑second loads or want to use a modern frontend framework (React, Vue, Svelte) the server becomes a bottleneck. The theme layer also ties your markup to Ghost’s template engine, making it harder to reuse components across projects.
Why the Content API Solves It
Ghost’s Content API is a read‑only endpoint that delivers posts, pages, and tags as JSON. By calling this API from an external build step or serverless function, you move rendering off the Ghost server and onto a static site generator (SSG) or a frontend framework. The API key is scoped to read‑only access, so exposing it in a build script does not grant administrative privileges.
Fetching Posts with JavaScript: A Worked Example
The following snippet shows how to retrieve the five most recent posts, including their tags, during a build step. Run this in a Node.js environment (or any JavaScript runtime) where you can keep the API key secret.
// Replace these values with your own
const GHOST_URL = 'https://your-blog.ghost.io';
const CONTENT_API_KEY = 'your_content_api_key_here';
async function getRecentPosts() {
const url = `${GHOST_URL}/ghost/api/content/posts/?key=${CONTENT_API_KEY}&limit=5&include=tags`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Ghost API error: ${response.status} ${response.statusText}`);
}
const json = await response.json();
return json.posts; // array of post objects
}
// Example usage in a build script
getRecentPosts()
.then(posts => {
// Pass `posts` to your SSG (e.g., Eleventy, Next.js) for rendering
console.log(`Fetched ${posts.length} posts`);
})
.catch(err => console.error('Failed to fetch from Ghost:', err));
Request Parameters for Efficiency
- limit: Caps the number of records returned. Omitting it can cause the API to attempt to send your entire archive, which spikes memory usage and slows the build.
- include: Pulls related entities (tags, authors) in the same request, reducing round‑trips. Example:
include=tags,authors. - filter: Uses Ghost’s server‑side filtering syntax to retrieve only posts that match a tag or date range, avoiding client‑side filtering of large arrays.
Trade‑offs and Limitations
Moving to a headless setup removes the instant preview you get with the default theme. After editing a post in Ghost admin, the frontend will not update until you trigger a new build (via a webhook, CI pipeline, or manual redeploy). Without a caching layer or SSG, hitting the Content API on every page load re‑introduces latency and adds load to the Ghost instance. Additionally, any custom Handlebars helpers or theme‑specific features must be reimplemented in your frontend code.
Verifying the Integration
Before writing frontend logic, confirm that the API key and endpoint are correct. From a terminal, run:
curl -s "https://your-blog.ghost.io/ghost/api/content/posts/?key=your_content_api_key_here&limit=1"
You should see a JSON object with a posts array containing one item. A 401 Unauthorized response means you used an Admin API key or the key is missing; a 404 indicates the URL path is incorrect. Check that the response includes the expected fields (id, title, html, tags) to ensure the schema matches your expectations.
Actionable Next Steps
- Generate a Content API key in Ghost Admin → Settings → Integrations.
- Add the key to your build environment (never commit it to a public repo).
- Implement the fetch function shown above, adjusting
limitandincludeto match your needs. - Pass the returned JSON to your SSG or frontend framework and map fields to components.
- Set up a webhook (Ghost Admin → Settings → Webhooks) that triggers your rebuild pipeline whenever a post is published or updated.
With these steps you retain Ghost’s powerful editor while gaining the performance and flexibility of a decoupled frontend.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.