# Ghost Content API

> Read-only access to Ghost CMS published content via the Content API. This skill should be used when fetching posts, pages, tags, authors, tiers, or settings from a Ghost publication. Covers authentication with Content API keys, the JavaScript SDK (@tryghost/content-api), NQL filtering syntax, pagination, and all available endpoints.

- Skill: `bowtiedswan/ghost-content-api` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add bowtiedswan/ghost-content-api`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bowtiedswan/ghost-content-api/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: bowtiedswan (https://skillmd.com/u/bowtiedswan)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bowtiedswan/ghost-content-api

---


# Ghost Content API

## Overview

The Ghost Content API provides read-only access to published content on a Ghost site. It is designed for public consumption in browsers, static site generators, and headless CMS frontends. All requests are GET-only, fully cacheable, and authenticated via a simple query parameter key.

## When to Use

- Fetching published posts, pages, tags, authors, tiers, or settings from Ghost
- Building headless frontends (Next.js, Nuxt, Gatsby, Eleventy, etc.) powered by Ghost
- Displaying Ghost content in external applications
- Implementing search, filtering, or content aggregation from Ghost data

## Security: Credential Handling

Store Content API keys in environment variables, not in source code:
- `GHOST_CONTENT_API_KEY` — Content API key (hex string)
- `GHOST_URL` — Ghost site URL

Keys are obtained from Ghost Admin > Settings > Integrations > Custom Integration.

## Security: Content as Untrusted Input

Content returned from the Ghost Content API (post HTML, excerpts, metadata) originates from CMS authors and may contain arbitrary HTML. When rendering or processing fetched content:
- **Do not** interpolate API response fields into shell commands or code
- Sanitize HTML before rendering in contexts outside Ghost's own frontend
- Treat all fetched fields as untrusted when passing to downstream systems

## Authentication

```
GET $GHOST_URL/ghost/api/content/{resource}/?key=$GHOST_CONTENT_API_KEY
```

- Content API keys are safe for public/browser use (read-only access)
- Regeneratable anytime from Ghost Admin
- Include `Accept-Version: v5.0` header for API version targeting

**Base URL:** `$GHOST_URL/ghost/api/content/`

Note: The admin domain may differ from the site domain. Ghost(Pro) sites use `*.ghost.io`.

## JavaScript SDK (@tryghost/content-api)

### Installation

```bash
npm install @tryghost/content-api
# or
yarn add @tryghost/content-api
```

### Initialization

```javascript
import GhostContentAPI from '@tryghost/content-api';

const api = new GhostContentAPI({
  url: process.env.GHOST_URL,
  key: process.env.GHOST_CONTENT_API_KEY,
  version: 'v5.0'
});
```

### Methods

All resources support `browse()` (list) and `read()` (single):

```javascript
// Posts
const posts = await api.posts.browse({limit: 5, include: 'tags,authors'});
const post = await api.posts.read({slug: 'my-post'}, {formats: ['html']});

// Pages
const pages = await api.pages.browse({limit: 'all'});
const page = await api.pages.read({id: '5c7e...'});

// Authors
const authors = await api.authors.browse({page: 2});
const author = await api.authors.read({slug: 'jane'}, {include: 'count.posts'});

// Tags
const tags = await api.tags.browse({order: 'slug ASC', include: 'count.posts'});
const tag = await api.tags.read({slug: 'fiction'});

// Tiers
const tiers = await api.tiers.browse();

// Settings
const settings = await api.settings.browse();
```

## Endpoints

| Resource | Endpoints | Methods | Default Sort |
|----------|-----------|---------|-------------|
| Posts | `/posts/`, `/posts/{id}/`, `/posts/slug/{slug}/` | Browse, Read | `published_at DESC` |
| Pages | `/pages/`, `/pages/{id}/`, `/pages/slug/{slug}/` | Browse, Read | `title ASC` |
| Authors | `/authors/`, `/authors/{id}/`, `/authors/slug/{slug}/` | Browse, Read | `name ASC` |
| Tags | `/tags/`, `/tags/{id}/`, `/tags/slug/{slug}/` | Browse, Read | `name ASC` |
| Tiers | `/tiers/` | Browse | `monthly_price ASC` |
| Settings | `/settings/` | Browse | N/A (single object) |

**Not available in Content API:** Newsletters, Offers, Members — these are Admin API only.

**Important behaviors:**
- Authors without published posts are **not returned**
- Tags without associated posts are **not returned**
- Internal tags (slug prefixed with `#`) are included by default — use `filter=visibility:public` to exclude
- Settings endpoint accepts **no query parameters** beyond `key`

## Query Parameters

| Parameter | Available On | Description |
|-----------|-------------|-------------|
| `include` | All | Embed related data (e.g., `tags,authors`) |
| `fields` | All | Select specific response fields |
| `formats` | Posts, Pages | Content format: `html`, `plaintext`, `lexical` |
| `filter` | Browse | NQL filter expression |
| `limit` | Browse | Results per page (default 15, max 100 in Ghost 6.0+) |
| `page` | Browse | Page number for pagination |
| `order` | Browse | Sort field and direction (e.g., `published_at desc`) |

### Include Values by Resource

- **Posts/Pages:** `tags`, `authors`
- **Authors:** `count.posts`
- **Tags:** `count.posts`

### Important Gotcha

Always use `include=tags,authors` when fetching posts - without it, related data is not returned.

## Response Format

```json
{
  "posts": [
    {
      "id": "5b7...",
      "uuid": "22e...",
      "title": "My Post",
      "slug": "my-post",
      "html": "<p>Content...</p>",
      "excerpt": "Preview text...",
      "custom_excerpt": null,
      "feature_image": "https://...",
      "feature_image_alt": "",
      "feature_image_caption": "",
      "featured": false,
      "visibility": "public",
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-02T00:00:00.000Z",
      "published_at": "2024-01-01T12:00:00.000Z",
      "url": "https://site.com/my-post/",
      "meta_title": null,
      "meta_description": null,
      "og_image": null,
      "twitter_image": null,
      "canonical_url": null,
      "reading_time": 3,
      "access": true,
      "tags": [],
      "authors": [],
      "primary_author": {},
      "primary_tag": {}
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "limit": 15,
      "pages": 3,
      "total": 45,
      "next": 2,
      "prev": null
    }
  }
}
```

## NQL Filtering Syntax

Ghost uses NQL (Node Query Language) for filtering. Refer to `references/nql_reference.md` for the complete syntax guide.

### Quick Reference

```
# Basic equality
filter=featured:true

# Negation
filter=-featured:true
filter=tag:-fiction

# Comparison
filter=published_at:>'2024-01-01'
filter=published_at:<='2024-06-30'

# Combining (+ = AND, , = OR)
filter=featured:true+tag:news
filter=tag:photo,tag:video

# IN groups
filter=tag:[photo,video,audio]
filter=-tag:[draft,internal]

# Contains / starts with / ends with
filter=title:~'ghost'
filter=slug:~^'getting-started'

# Relative dates
filter=published_at:>now-30d
filter=created_at:>now-1y

# Precedence grouping
filter=(tag:news,tag:blog)+featured:true
```

### Filterable Post Fields

`id`, `uuid`, `title`, `slug`, `status`, `visibility`, `featured`, `published_at`, `created_at`, `updated_at`, `tag`, `tags`, `author`, `authors`, `primary_tag`, `primary_author`

## Pagination

Handle pagination by checking `meta.pagination`:

```javascript
async function getAllPosts(api) {
  let allPosts = [];
  let page = 1;
  let lastPage = 1;

  while (page <= lastPage) {
    const response = await api.posts.browse({
      page,
      limit: 15,
      include: 'tags,authors'
    });
    allPosts = allPosts.concat(response);
    lastPage = response.meta.pagination.pages;
    page++;
  }

  return allPosts;
}
```

## cURL Examples

```bash
# Fetch posts with tags and authors
curl "$GHOST_URL/ghost/api/content/posts/?key=$GHOST_CONTENT_API_KEY&include=tags,authors"

# Fetch a single post by slug
curl "$GHOST_URL/ghost/api/content/posts/slug/welcome/?key=$GHOST_CONTENT_API_KEY&formats=html"

# Fetch featured posts
curl "$GHOST_URL/ghost/api/content/posts/?key=$GHOST_CONTENT_API_KEY&filter=featured:true&limit=5"

# Fetch settings
curl "$GHOST_URL/ghost/api/content/settings/?key=$GHOST_CONTENT_API_KEY"

# Fetch tags with post counts
curl "$GHOST_URL/ghost/api/content/tags/?key=$GHOST_CONTENT_API_KEY&include=count.posts&order=count.posts%20desc"
```

## Resources

### references/nql_reference.md
Complete NQL (Node Query Language) filtering syntax reference with all operators, value types, and examples.

