Ghost
Available · Tier 1The 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
| Feature | Supported | Notes |
|---|---|---|
| Publish + update + unpublish | Yes | Full CRUD via Ghost Admin API. |
| Native scheduling | Yes | status: "scheduled" + published_at. Ghost’s built-in scheduler does the flip. |
| Draft mode | Yes | status: "draft". Available in Ghost admin under Posts → Drafts. |
| JSON-LD in <head> | Yes | codeinjection_head — first-class API field, no theme work needed. |
| Featured image upload | Yes | We POST to /admin/images/upload first, then attach the returned URL. |
| Tags | Yes | Created on the fly if names don't exist; tag IDs cached for re-use. |
| Authors | Yes | Resolved by slug. The Admin API user must have permission to assign authors. |
| Meta + canonical + OG | Yes | All native fields on the post resource — no plugin needed. |
| Members-only / paid posts | Partial | visibility: "public" by default. Switch to "members" / "paid" by setting adapter_metadata.visibility on the connection. |
| Mobiledoc + Lexical body | Partial | We 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 | — No | We don't trigger Ghost's newsletter automation by default. Toggle in Ghost admin per post if needed. |
| Articles + FAQ pages | Yes | Publishes 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. |
Setup
- 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. - Admin API key — the one we need (format:
- 2
Connect inside SEORAV
Integrations → Connect → Ghost. Paste:
• Site URL— your blog’s root (no trailing slash)
• Admin API key — theid:secretstring - 3
Probe runs
We mint a JWT, hit
/admin/site/, verify the integration user hasposts.write+images.uploadpermissions. Ghost’s integrations API gives all custom integrations full access by default — this almost always passes.
What we send to Ghost
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
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
| Property | Type | Description |
|---|---|---|
| title, slug, html, excerpt | string | Direct mapping from CanonicalPost. We ship body as HTML; Ghost converts to Lexical for the editor. |
| status | enum | One of published / scheduled / draft. Driven by publish_mode on the SEORAV side. |
| feature_image, feature_image_alt | string | Hero URL + alt text. Ghost downloads + re-hosts on its own CDN at /content/images/. |
| meta_title, meta_description, canonical_url | string | Native fields on every Ghost post — no plugin needed. |
| og_title, og_description, og_image, twitter_* | string | Ghost has separate og_ and twitter_ fields. We populate both with the same content unless you override. |
| tags | array | Sent as [ {name} ]. Ghost creates new tags automatically and assigns IDs. |
| authors | array | Sent as [ {slug} ]. The author must exist in Ghost — we don’t auto-create users. |
| codeinjection_head | string | The 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
- 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’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
- Use a dedicated custom integration for SEORAV — don’t reuse one already used by another tool.
- If you self-host Ghost, run on HTTPS only. We refuse
http://. - Rotate the API key once per quarter as a hygiene practice — Ghost’s “Regenerate” button keeps the integration name + member visibility, just rotates the secret. Update SEORAV after rotating.
- Keep Ghost up-to-date — major versions sometimes change API field names (theme imports broke in 5.0; Mobiledoc → Lexical in 5.x).