A zero-dependency Ghost Admin API client and a
ghost-clifor managing members and posts.
This package contains two things:
api.js— a small Ghost Admin API client built on the native Node.jsfetchandcryptomodules. Noaxios, noform-data, nojsonwebtoken— nothing to install.cli.js(ghost-cli) — a command line tool for managing Ghost members (list, add, remove, subscribe, unsubscribe) and posts (list, add, edit).
- Node.js >= 22 (for the global
fetchAPI).
The CLI reads connection details from flags or environment variables:
| Setting | Flag | Environment variable | Default |
|---|---|---|---|
| Site URL | --url |
GHOST_API_URL |
— (required) |
| Admin API key | --key |
GHOST_ADMIN_API_KEY |
— (required) |
| API version | --version |
GHOST_API_VERSION |
v5.0 |
The site URL may be either the site root (https://example.com) or a full Admin
API URL (https://example.com/ghost/api/admin/) — only the origin is used.
The Admin API key has the form id:secret and is created in Ghost under
Settings → Advanced → Integrations → Add custom integration.
export GHOST_API_URL="https://example.com/ghost/api/admin/"
export GHOST_ADMIN_API_KEY="0123...abcd:0123...def0"ghost-cli <group> <command> [arguments] [options]| Command | Description |
|---|---|
members list |
List members, newest first (auto-paginates, see below) |
members add <email> |
Create a new member |
members remove <email> |
Delete a member permanently |
members subscribe <email> |
Subscribe a member to all active newsletters |
members unsubscribe <email> |
Unsubscribe a member from all newsletters |
Member options: --name, --note, --labels a,b,c, --no-subscribe (for add);
--limit, --page, --filter (for list).
ghost-cli members list --limit all
ghost-cli members add jane@example.com --name "Jane Doe" --labels vip,beta
ghost-cli members unsubscribe jane@example.com
ghost-cli members remove jane@example.com| Command | Description |
|---|---|
post list |
List posts (scope with a section flag) |
post add --title "..." |
Create a post |
post edit <id|slug> |
Update an existing post |
Section flags map to the internal tags that separate content on the site:
| Flag | Tag | Use |
|---|---|---|
--blog |
#blog |
Blog posts |
--docs |
#docs |
Documentation |
--changelog |
changelog |
Release notes |
On list a section flag filters by that tag; on add/edit it attaches the
tag (existing tags are preserved on edit).
Post options:
| Option | Description |
|---|---|
--title <title> |
Post title (required for add) |
--markdown <file|text> |
Body from a Markdown file path or inline Markdown text |
--html <file|text> |
Body from an HTML file path or inline HTML (sent with ?source=html) |
--content <text> |
Inline Markdown body (alias of --markdown text) |
--meta-description <text> |
SEO meta description |
--cover <url> |
Cover image (feature_image) — URL only (see note) |
--excerpt <text> |
Custom excerpt |
--tags <a,b,c> |
Extra tags, added alongside the section tag |
--status <draft|published> |
Status (default: draft) |
# Import a Markdown doc as a published docs page
ghost-cli post add --docs --title "Install guide" --markdown ./install.md --status published
# New blog post from inline Markdown with a cover and meta description
ghost-cli post add --blog --title "We launched" \
--content "Today we **shipped** it." \
--cover "https://cdn.example.com/launch.png" \
--meta-description "Our launch announcement"
# List and edit
ghost-cli post list --docs --limit 20
ghost-cli post edit install-guide --title "Installation guide" --meta-description "How to install"Cover images are set by URL only. This client intentionally does not use
FormData/multipart, so it cannot upload a local image file. Pass--covera URL that is already hosted (e.g. an existing Ghost/CDN image).
members list and post list accept --limit <n|all>. Ghost caps each request
at 100 items; when --limit is greater than 100 (or all), the CLI transparently
fetches successive pages and trims to the requested count. --page sets the
starting page.
Every command accepts --json to print the raw API response instead of a table —
handy for piping into jq or feeding other tools.
const GhostAdminAPI = require("./api");
const api = GhostAdminAPI({
url: "https://example.com",
key: process.env.GHOST_ADMIN_API_KEY,
version: "v5.0"
});
const posts = await api.posts.browse({ limit: 5, filter: "tag:hash-docs" });
console.log(posts);The client exposes the standard Ghost Admin resources (posts, pages, tags,
webhooks, members, users, newsletters) with browse, read, add,
edit, and delete methods, plus site.read(), config.read(), and
themes.activate(name).
Note: file/image/theme upload endpoints are intentionally omitted — this client does not use
FormData/multipart requests.
- Auth — HS256 JWTs are signed on the fly with Node's
crypto.createHmac, scoped to a 5-minute expiry and the/admin/audience, exactly as the Ghost Admin API expects. - Transport — every request goes through the native
fetch. Query parameters are comma-joined and URI encoded the way Ghost's NQL filters need. - Markdown import — Markdown is wrapped in a Mobiledoc markdown card, which Ghost renders to its native format on save (no external Markdown parser needed).
- Edits —
post editfirst reads the post to obtain itsupdated_at, which Ghost requires for optimistic-concurrency collision detection. - Errors — non-2xx responses are unwrapped into a normal
Errorwhosemessageandnamecome from Ghost'serrors[]payload.