Ghost logo

Ghost

Available · Tier 1

The cleanest of the lot. Ghost’s Admin API is well-designed, JWT auth means tokens are short-lived (5 min), and the per-post `codeinjection_head` field is a first-class place to put JSON-LD without theme edits. If you’re a serious publisher and don’t need WordPress’s plugin universe, Ghost is the easiest setup we ship.

How it works

On every publish, SEORAV mints a fresh JWT signed with your Admin API key, attaches it as Authorization: Ghost {jwt}, and POSTs to /ghost/api/admin/posts/ with ?source=htmlso we can ship pre-rendered HTML directly. JSON-LD goes into Ghost’s native codeinjection_head field — Ghost renders it inside <head> on the published page automatically. No theme edits required.

Tokens are minted per-request, valid for 5 minutes, and never stored. The Admin API key (a 26-char string of the form id:secret) is what we encrypt and keep — its rotation lifetime is up to you.

What we support

FeatureSupportedNotes
Publish + update + unpublish YesFull CRUD via Ghost Admin API.
Native scheduling Yesstatus: "scheduled" + published_at. Ghost&rsquo;s built-in scheduler does the flip.
Draft mode Yesstatus: "draft". Available in Ghost admin under Posts → Drafts.
JSON-LD in <head> Yescodeinjection_head — first-class API field, no theme work needed.
Featured image upload YesWe POST to /admin/images/upload first, then attach the returned URL.
Tags YesCreated on the fly if names don't exist; tag IDs cached for re-use.
Authors YesResolved by slug. The Admin API user must have permission to assign authors.
Meta + canonical + OG YesAll native fields on the post resource — no plugin needed.
Members-only / paid posts Partialvisibility: "public" by default. Switch to "members" / "paid" by setting adapter_metadata.visibility on the connection.
Mobiledoc + Lexical body PartialWe send HTML (?source=html). Ghost converts to Lexical internally. Editor will show our content as a single Lexical block — fine for reading, slightly awkward to edit.
Newsletter delivery NoWe don't trigger Ghost's newsletter automation by default. Toggle in Ghost admin per post if needed.
Articles + FAQ pages YesPublishes not only blog articles but also FAQ / answer-engine pages. One connection per content type — create one Ghost connection for articles and a second for answer pages if you want both surfaces. Pin each to a different tag set so they stay separate in the editor.
One connection per content type
Ghost connections are scoped to a single content type. To publish both long-form articles AND FAQ / answer-engine pages from the same SEORAV workspace, add two Ghost connections — one for articles, one for answer pages. The same Admin API key works for both; differentiate them with distinct tags so the two surfaces stay separate in the editor.

Setup

  1. 1

    Create a Custom Integration

    In Ghost: Settings → Integrations → Add custom integration. Name it SEORAV. Ghost reveals three credentials:

    • Admin API key — the one we need (format: id:secret)
    • Content API key — read-only, not used by SEORAV
    • API URL — typically https://yourblog.ghost.io
    The Admin API key gives full write access to your blog. Treat it like a password — paste once into SEORAV, then forget. We encrypt it at rest.
  2. 2

    Connect inside SEORAV

    Integrations → Connect → Ghost. Paste:
    Site URL— your blog’s root (no trailing slash)
    Admin API key — the id:secret string

  3. 3

    Probe runs

    We mint a JWT, hit /admin/site/, verify the integration user has posts.write + images.upload permissions. Ghost’s integrations API gives all custom integrations full access by default — this almost always passes.

What we send to Ghost

http
POST https://yourblog.ghost.io/ghost/api/admin/posts/?source=html
Authorization: Ghost {jwt}        # short-lived, signed with admin API key
Content-Type: application/json

{
  "posts": [{
    "title":      "How to choose a reverse-osmosis system in 2026",
    "slug":       "how-to-choose-reverse-osmosis-system-2026",
    "html":       "<h2>What you actually need to know</h2><p>…</p>",
    "excerpt":    "Membrane stages, recovery rate, remineralisation — three specs that matter.",
    "status":     "published",
    "feature_image":     "https://cdn.seorav.com/articles/9b1c5e0a/hero.webp",
    "feature_image_alt": "Three reverse-osmosis systems on a kitchen counter",
    "meta_title":        "Reverse osmosis · the 3 specs that matter (2026)",
    "meta_description":  "Membrane stages, recovery rate, remineralisation. Plain-English …",
    "canonical_url":     "https://yoursite.com/blog/how-to-choose-reverse-osmosis-system-2026",
    "og_title":          "Reverse osmosis · the 3 specs that matter",
    "og_image":          "https://cdn.seorav.com/articles/9b1c5e0a/hero.webp",
    "og_description":    "Membrane stages, recovery rate, remineralisation. …",
    "tags":     [ {"name": "reverse osmosis"}, {"name": "water filtration"}, {"name": "buying guide"} ],
    "authors":  [ {"slug": "elena-vance"} ],
    "codeinjection_head": "<script type=\"application/ld+json\">…</script>\n<script type=\"application/ld+json\">…</script>"
  }]
}

A successful response

json
HTTP/1.1 201 Created
{
  "posts": [{
    "id":     "67c5fe1234567890abcd1234",
    "uuid":   "a3f8…",
    "title":  "How to choose a reverse-osmosis system in 2026",
    "slug":   "how-to-choose-reverse-osmosis-system-2026",
    "status": "published",
    "url":    "https://yourblog.ghost.io/how-to-choose-reverse-osmosis-system-2026/",
    "published_at": "2026-04-27T08:00:00.000Z",
    "feature_image": "https://yourblog.ghost.io/content/images/2026/04/hero.webp"
  }]
}

Field mapping

PropertyTypeDescription
title, slug, html, excerptstringDirect mapping from CanonicalPost. We ship body as HTML; Ghost converts to Lexical for the editor.
statusenumOne of published / scheduled / draft. Driven by publish_mode on the SEORAV side.
feature_image, feature_image_altstringHero URL + alt text. Ghost downloads + re-hosts on its own CDN at /content/images/.
meta_title, meta_description, canonical_urlstringNative fields on every Ghost post — no plugin needed.
og_title, og_description, og_image, twitter_*stringGhost has separate og_ and twitter_ fields. We populate both with the same content unless you override.
tagsarraySent as [ {name} ]. Ghost creates new tags automatically and assigns IDs.
authorsarraySent as [ {slug} ]. The author must exist in Ghost — we don’t auto-create users.
codeinjection_headstringThe JSON-LD blocks joined into one string. Ghost emits them in <head> on the live page.

Publishing options

Auto-publish (default)

status: "published"goes live immediately. Ghost respects the slug we set; if you’ve customised your URL prefix in Settings → Design, the live URL will reflect that.

Scheduled

status: "scheduled" + published_at: <ISO8601>. Ghost has rock-solid scheduling — you don’t need WP-Cron-style hacks. The post flips to published at the requested minute, even on a quiet site.

Draft

status: "draft". Visible only to staff in Ghost admin under Posts → Drafts. Approve manually to publish.

Members-only / paid

Set visibility in adapter metadata to members or paid. We send visibility in the post payload and Ghost gates rendering accordingly. Useful if SEORAV-generated content is part of your paid tier.

Activity timeline · what you’ll see

Activity timeline · what shows up in your dashboard
  • job_queuedidempotency_key=8b3c…
  • adapter_requestPOST/admin/images/upload (feature image)
  • adapter_response201843ms
  • adapter_requestPOST/admin/posts/?source=html
  • adapter_response201491mspost_id=67c5fe…
  • verify_startGET https://yourblog.ghost.io/how-to-…
  • verify_complete200198msscore 99/100 · all schema graphs in <head>
  • job_succeededcleanest publish path we ship

After publishing — make it shine

Don&rsquo;t edit the HTML in the Lexical editor

Ghost converts our HTML into a single Lexical “HTML card” on first import. The card is editable — but if you switch to the rich-text editor mid-card, Ghost re-parses and may strip JSON-LD scripts. If you need to edit, edit in raw HTML mode (toggle in the card menu).

Verify the codeinjection_head landed

Open the live URL → View source → search for application/ld+json. Should appear inside <head>. If it’s in body, your theme overrides Ghost’s default {{ghost_head}} helper — fix the theme.

Set the post-feature image as social card

Ghost auto-uses feature_image for OG + Twitter cards if og_image isn’t set. We send both for safety, but double-check in Ghost’s “Settings → Social cards” preview. LinkedIn caches old images aggressively — use their Post Inspector to flush.

Pin to a primary tag

Ghost considers the first tag the “primary tag” — drives URL prefix and theme styling. Make sure SEORAV is sending tags in the order that matches your taxonomy strategy.

Newsletter on/off

By default we don’t trigger Ghost’s newsletter send. If you want every SEORAV publish to email subscribers, enable Ghost’s “Auto-send newsletter on publish” in Settings → Email newsletter.

Submit to IndexNow on publish

Ghost doesn’t ship a native IndexNow trigger, so the cleanest path is a small webhook from Ghost → an endpoint that POSTs to IndexNow with the new URL. Bing and Yandex pick it up within minutes — much faster than waiting for the next crawl.

Troubleshooting

401 / “Authorization failed”

Three causes. (1) Your Admin API key is wrong — make sure you copied bothhalves separated by the colon. (2) Your Ghost version is older than 4.x — Admin API JWT auth was added in 4.0. (3) System clock skew > 5 min between our server and your Ghost instance — Ghost rejects JWTs whose iatis too far off. Sync your server’s NTP.

Image upload returns 413 / 422

Ghost rejects images larger than 1MB on shared hosting plans. SEORAV-generated heroes are usually 200-500 KB, but if you’ve customised our images to higher resolution, increase your Ghost image_optimization.contentImageSize setting in config.production.json or use Ghost(Pro) which lifts the limit.

Canonical URL not appearing on the page

Ghost emits <link rel="canonical"> only when canonical_urldiffers from the post’s default URL. We send it always; Ghost suppresses if it matches. Click View source on the live URL — if your custom domain matches the canonical, no tag is rendered (this is correct behaviour, not a bug).

Author not assigned

We send authors by slug. If the author slug doesn’t exist in Ghost, the post falls back to the integration user (named after your custom integration). Create the author in Ghost admin → Staff with a slug that matches what SEORAV is sending.

Security & best practices

Storage
Admin API key encrypted with AES-256-GCM. JWTs are minted fresh per request, valid for 5 minutes, and never persisted. If your key leaks, revoke at Ghost → Settings → Integrations → SEORAV → Regenerate keys; SEORAV will start failing immediately.

Useful links