> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Ghost Integration

> Publish GEO-optimized content from Siftly directly to your Ghost blog using the Ghost Admin API.

<Frame>
  <img src="https://mintcdn.com/siftly/IwND2vAxRbhCiRev/images/cms-integrations.svg?fit=max&auto=format&n=IwND2vAxRbhCiRev&q=85&s=2704e9affce00587d23137bf707339c7" alt="Supported CMS platforms including Ghost" className="rounded-xl" width="900" height="280" data-path="images/cms-integrations.svg" />
</Frame>

## Overview

This guide is for Ghost users who want to publish Siftly-generated GEO content and apply Siftly's SEO/GEO field recommendations. After following it, you'll be able to push optimized content to Ghost and understand where each meta field lives in the platform.

**Difficulty:** ✅ Non-tech friendly — Ghost has built-in SEO fields, no plugins needed.

Ghost is an open-source publishing platform built for professional blogs and newsletters. The Siftly–Ghost integration connects to your Ghost site via the **Ghost Admin API** and publishes content directly — either as a live post or a draft — using the unified 3-tier field mapping system.

This integration is ideal for teams running Ghost-powered blogs who want to close the loop between SEO/GEO analysis and content execution inside a single workflow.

***

## Prerequisites

Before connecting, you'll need:

* A Ghost site (Ghost Pro or self-hosted Ghost 5.x+)
* **Admin** access to your Ghost dashboard
* A **Custom Integration** created in Ghost (see Step 1)

<Note>
  The Ghost Admin API is available on all Ghost plans, including Ghost Pro and self-hosted installations. There is no plan restriction.
</Note>

***

## Step 1: Create a Custom Integration in Ghost

Ghost uses **Custom Integrations** to generate API credentials for third-party tools like Siftly.

1. Log in to your Ghost Admin dashboard (e.g., `https://yoursite.ghost.io/ghost`)
2. Go to **Settings** (bottom-left gear icon)
3. Click **Integrations**
4. Scroll to the bottom and click **Add custom integration**
5. Give it a name — e.g., **Siftly**
6. Click **Create**

After creation, Ghost displays four pieces of information. You need **two** of them:

| Field             | Where to find it                 | Example                              |
| ----------------- | -------------------------------- | ------------------------------------ |
| **Admin API key** | Shown under the integration name | `69d9c7eeb369ab00013a5f2b:dde97d...` |
| **API URL**       | Shown below the Admin API key    | `https://yoursite.ghost.io`          |

<Warning>
  The **Admin API key** grants full read/write access to your Ghost site, including image uploads to the Ghost media library — treat it like a password. Never share it publicly or commit it to source control. Siftly encrypts and stores it securely at rest.
</Warning>

<Note>
  The Admin API includes image upload access. Siftly uploads hero images and inline content images to Ghost's media library automatically at publish time — no additional configuration needed.
</Note>

The Admin API key is in the format `id:hex_secret` separated by a colon. Copy the entire string including both parts.

***

## Step 2: Connect Ghost in Siftly

1. In Siftly, go to **Settings → Integrations**
2. Under **CMS Integrations**, click **Ghost**
3. Enter your:
   * **API URL** — the base URL of your Ghost site (e.g., `https://yoursite.ghost.io`)
   * **Admin API key** — the full `id:hex_secret` string copied from Ghost
   * **Blog Base URL** *(optional)* — the public-facing URL of your blog (e.g., `https://mycompany.com/blog`)
4. Click **Connect Ghost**

Siftly validates your credentials by making a test call to the Ghost Admin API. If validation succeeds, you'll see the **Connected** screen with your site URL confirmed.

<Tip>
  If your Ghost site is on a custom domain (e.g., `https://blog.mycompany.com`), use that as the API URL — not the `.ghost.io` subdomain, unless that's your primary domain.
</Tip>

### Blog Base URL (optional)

The **Blog Base URL** field lets you set the public-facing domain for your blog, separate from the Ghost Admin API URL. This is useful when:

* Your Ghost instance is hosted at `yoursite.ghost.io` but your public blog lives at `mycompany.com/blog`
* You want Siftly to track and display the correct canonical post URL after publishing

When set, Siftly builds the published post URL as:

```
{Blog Base URL}/{slug}/
```

For example, if your Blog Base URL is `https://mycompany.com/blog` and your post slug is `intro-to-geo`, the tracked URL will be `https://mycompany.com/blog/intro-to-geo/`.

If left blank, Siftly uses the post URL returned directly by the Ghost API.

***

## How Ghost authentication works

Ghost's Admin API does not accept plain API keys in HTTP headers. Instead, it requires a **short-lived JWT token** signed from your Admin API key on every request. Siftly handles this automatically:

1. When you publish, Siftly splits your Admin API key into its `id` and `hex_secret` parts
2. It generates a JWT token signed with **HS256** using `bytes.fromhex(hex_secret)` as the signing key
3. The JWT carries a 5-minute expiry (`exp = iat + 300`) and the audience claim `"/admin/"`
4. Each request uses a freshly generated token — no token caching or refresh needed

This is handled entirely server-side. Your Admin API key never leaves Siftly's backend after being saved.

***

## Field Mapping

Ghost uses the unified 3-tier field mapping system. Ghost's built-in fields have fixed API names, so the required fields are pre-configured.

### Required Fields

| Siftly Field | Ghost Post Field | Notes                                         |
| ------------ | ---------------- | --------------------------------------------- |
| **Title**    | `title`          | Pre-configured — Ghost's built-in title field |
| **Slug**     | `slug`           | Pre-configured — Ghost's built-in slug field  |
| **Body**     | `html`           | Pre-configured — sent via `?source=html`      |

### Optional Fields

When mapped, these are auto-populated from your content data.

| Siftly Field         | Ghost Post Field | Notes                                      |
| -------------------- | ---------------- | ------------------------------------------ |
| **Meta Title**       | `meta_title`     | SEO page title                             |
| **Meta Description** | `custom_excerpt` | Truncated to 300 characters                |
| **Keywords**         | `tags`           | Up to 10 tags, created if they don't exist |
| **Categories**       | —                | Not natively supported by Ghost            |
| **JSON-LD**          | —                | Not natively supported by Ghost            |
| **Word Count**       | —                | Not natively supported by Ghost            |

<Note>
  Ghost has a fixed post schema — you map optional fields to the available Ghost post fields. Fields marked with "—" are not supported by Ghost's API and will be skipped.
</Note>

### HTML content handling

Siftly wraps the post body in Ghost's **HTML card** comments before sending:

```html theme={null}
<!--kg-card-begin: html-->
<your content here>
<!--kg-card-end: html-->
```

This preserves your HTML exactly as Siftly generated it, preventing Ghost's Lexical editor from modifying headings, lists, links, or custom formatting during the conversion process.

***

## Custom Fields

Add extra fields your Ghost posts require that aren't part of Siftly's standard content schema. Custom fields are sent as additional properties in the Ghost API payload.

### Field Types

| Type    | Description                  | Example                           |
| ------- | ---------------------------- | --------------------------------- |
| String  | Plain text                   | Author name, subtitle             |
| Number  | Numeric value                | Priority, reading time            |
| Boolean | True/false                   | Featured post, comments enabled   |
| JSON    | Structured object            | SEO config, social media metadata |
| Select  | Pick from predefined options | Category, status, content type    |

### Select Options with Separate Labels and Values

When adding a **Select** custom field, you define the available options in a textarea. Each option can have a separate **value** (sent to CMS) and **label** (shown in the dropdown):

```
value:label
```

**Example — Labels differ from values:**

```
1:Health
2:Tech
3:Food
4:Fashion
```

The dropdown shows `Health`, `Tech`, etc., but Siftly sends `1`, `2`, etc. to the CMS API.

**Example — Labels same as values:**

```
food
tech
health
fashion
```

When no `:` is present, the option text is used as both the label and value. This is backward-compatible with existing configurations.

<Tip>
  Use the `value:label` format when your CMS expects specific IDs, slugs, or enum values that differ from the human-readable names shown to content editors.
</Tip>

### Default Values

Set a default value when adding a custom field. This value pre-fills at publish time — you can override it for each post.

### Use Existing Field

Instead of setting a manual default, you can link a custom field to an existing content field. For example, map Ghost's `og_description` field to use the same value as **Meta Description**.

This is useful when your Ghost setup has multiple fields that should contain the same data (e.g., `custom_excerpt` and `og_description` both using your meta description).

***

## Publishing

### Draft vs Published

When publishing, choose between:

* **Draft** — content is pushed to Ghost as a draft post for review in Ghost Admin
* **Published** — the post is created and immediately published to your live site

### Custom Fields at Publish Time

If you've defined custom fields, they appear in the publish dialog pre-filled with their default values (or auto-filled from linked fields). You can edit them before publishing.

Standard fields (title, slug, body, meta data, keywords) are auto-populated from your content — no manual input needed.

***

## After publishing

Once published, the post appears on your Ghost site (immediately if you chose Published, or in Ghost Admin drafts if you chose Draft). From Siftly you can:

* **View the post** — click the published URL link in the content editor
* **Track performance** — the post URL is saved to Siftly for citation and visibility analysis

To make further edits, go to your Ghost Admin dashboard → **Posts** → find the post and edit it in Ghost's editor.

<Note>
  Siftly does not support updating an existing Ghost post. Each publish creates a new post. To revise published content, edit it directly in Ghost Admin.
</Note>

***

## Where to apply Siftly's recommendations on Ghost

Ghost has built-in SEO fields on every post — no plugins required. Here's where each field lives and how Siftly's recommendations map to them.

### Meta Title

**Location:** Post editor → click the gear icon (⚙️) → **Meta data → Meta title**

Ghost renders this as the `<title>` tag and OG title. If left empty, Ghost uses the post title.

Siftly auto-pushes the meta title to Ghost's `meta_title` field when you map it in your integration settings.

### Meta Description

**Location:** Post editor → gear icon (⚙️) → **Meta data → Meta description**

Ghost renders this as `<meta name="description">` and `og:description`. If left empty, Ghost auto-generates an excerpt from the first 500 characters of your post.

Siftly maps this to `custom_excerpt` by default, which Ghost uses as both the visible excerpt and the meta description.

### Open Graph Image / Social Share Image

**Location:** Post editor → gear icon (⚙️) → **Twitter card** and **Facebook card** sections

Ghost supports separate OG images for Twitter and Facebook. Upload or paste an image URL in each section.

<Tip>
  Ghost also uses the **Feature image** (the hero image at the top of the post) as the default OG image if no specific social images are set. Set a Feature Image and you get social sharing coverage automatically.
</Tip>

### Canonical URL

**Location:** Post editor → gear icon (⚙️) → **Meta data → Canonical URL**

Ghost auto-generates a canonical URL pointing to the post's own URL. Override this field only for syndicated or cross-posted content.

<Note>
  Siftly does not push canonical URL overrides. Ghost's default canonical (the post permalink) is correct for original content.
</Note>

### Tags & Categories

**Location:** Post editor → gear icon (⚙️) → **Tags**

Ghost uses **Tags** for both categories and tags (there is no separate category system). The first tag is treated as the "primary tag" and often used by themes for navigation.

Siftly pushes keywords as Ghost tags automatically. Tags that don't exist are created. Up to 10 tags per post.

### JSON-LD Structured Data

**Ghost auto-generates Article JSON-LD** for every post. This includes:

* `@type: Article`
* `headline`, `datePublished`, `dateModified`
* `author` with name and URL
* `publisher` with logo
* `image` from the feature image

**You do NOT need to add Article schema manually.** Ghost handles it.

**Supplemental schema (FAQ, HowTo, Product) — pushed automatically by Siftly:**

When you map Siftly's **JSON-LD** field in your integration settings, Siftly wraps your JSON-LD in a `<script type="application/ld+json">` tag and pushes it directly to Ghost's `codeinjection_head` field on publish. This renders in the `<head>` of the post page automatically.

No manual Code Injection editing is needed when the JSON-LD field mapping is configured.

<Warning>
  Do NOT add an Article schema via Siftly's JSON-LD field — Ghost already generates one. Siftly's JSON-LD recommendations are designed to supplement with FAQ, HowTo, or Product schema, not duplicate the base Article schema.
</Warning>

<Note>
  If you prefer to add JSON-LD manually (or need to edit what Siftly pushed), go to: Post editor → gear icon (⚙️) → **Code injection → Post Header**. The `<script>` tag will already be there from Siftly's push.
</Note>

### Keywords

Ghost does not render a `<meta name="keywords">` tag. Keywords are handled through the **Tags** system, which Ghost exposes in its structured data and RSS feed.

Siftly maps content keywords to Ghost tags automatically.

***

## Pushing Siftly recommendations automatically

Siftly pushes all SEO fields to Ghost in a single publish action when mapped:

<Steps>
  <Step title="Generate content in Siftly">
    Use Siftly's content editor to generate or optimize your article. Siftly produces meta title, meta description, keywords, and JSON-LD recommendations.
  </Step>

  <Step title="Click Publish → Ghost">
    In the content editor, click **Publish** and select **Ghost**. Choose Draft or Published status.
  </Step>

  <Step title="All mapped fields pushed automatically">
    Title, slug, body (HTML), meta title (`meta_title`), meta description (`meta_description`), excerpt (`custom_excerpt`), tags, and JSON-LD (`codeinjection_head`) are all sent in one API call. No manual copy-paste needed.
  </Step>
</Steps>

<Tip>
  When JSON-LD is mapped, Siftly automatically wraps it in a `<script type="application/ld+json">` tag and pushes it to Ghost's `codeinjection_head` field. This renders in the `<head>` of your post page without any manual Code Injection editing.
</Tip>

***

## Platform-specific quirks & limitations

<Warning>
  **Article JSON-LD is auto-generated.** Ghost produces its own Article structured data. Do NOT add competing Article schema via Code Injection or you'll get duplicate schema warnings.
</Warning>

<Note>
  **No categories system.** Ghost uses Tags only. The first tag assigned to a post becomes the "primary tag," which many Ghost themes use for navigation sections. Order your Siftly keywords with the most important one first.
</Note>

* **Code Injection scope:** Siftly pushes JSON-LD to per-post `codeinjection_head`. Ghost also offers site-wide Code Injection (Settings → Code Injection) — use that for Organization schema that applies to all pages.
* **No native Open Graph override via API:** Ghost's Admin API does not accept `og_image` directly on post creation. Feature images serve as OG images. Set your feature image after publishing from Siftly.
* **Excerpt length:** Ghost truncates `custom_excerpt` at 300 characters. Siftly's meta description is auto-trimmed to fit.
* **Members-only content:** If you use Ghost's membership features, posts published via Siftly default to "Public" visibility. Change visibility to "Members" or "Paid" in Ghost Admin after publishing if needed.

***

## Validating the setup

After publishing your first post with Siftly's recommendations:

<Steps>
  <Step title="View the live page source">
    Open your published Ghost post in a browser. Right-click → **View Page Source**. Search for:

    * `<title>` — should contain your Siftly meta title (or post title if no meta title was set)
    * `<meta name="description"` — should contain your meta description
    * `<script type="application/ld+json">` — should show Ghost's auto-generated Article schema plus any supplemental schema you added
  </Step>

  <Step title="Run Google Rich Results Test">
    Go to [Google Rich Results Test](https://search.google.com/test/rich-results) and paste your post URL. Confirm Article schema is valid and any FAQ/HowTo schema is detected.
  </Step>

  <Step title="Run Schema.org Validator">
    Go to [Schema.org Validator](https://validator.schema.org/) and paste your post URL. Check for errors or warnings.
  </Step>

  <Step title="Check social sharing">
    Use [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/) to confirm OG image, title, and description. Ghost generates these from your post's feature image and meta data.
  </Step>
</Steps>

***

## Difficulty & setup recap

<Card title="Ghost Setup Summary" icon="ghost">
  | Aspect                  | Rating                                                                                       |
  | ----------------------- | -------------------------------------------------------------------------------------------- |
  | **Initial setup**       | ✅ Easy — Admin API key, no OAuth flow                                                        |
  | **Applying SEO fields** | ✅ Easy — Ghost has built-in meta title/description fields                                    |
  | **JSON-LD**             | ✅ Auto-generated Article schema; ✅ Siftly pushes supplemental schema to `codeinjection_head` |
  | **Ongoing editing**     | ✅ Easy — publish from Siftly, edit in Ghost Admin                                            |
  | **Developer needed?**   | No                                                                                           |
</Card>

***

## Related

<CardGroup cols={2}>
  <Card title="CMS Integrations Overview" icon="grid-2" href="/integrations/overview">
    Compare all supported platforms and the unified field mapping system.
  </Card>

  <Card title="Quickstart Guide" icon="rocket" href="/quickstart">
    Set up your brand and run your first GEO analysis.
  </Card>

  <Card title="Content Generation" icon="file-pen" href="/features/content-generation">
    How Siftly generates GEO-optimized content and recommendations.
  </Card>

  <Card title="Choosing a CMS for AI Visibility" icon="book" href="https://siftly.ai/blog/cms-and-ai-visibility">
    Our guide to picking the right CMS for GEO optimization.
  </Card>
</CardGroup>

***

## Disconnect Ghost

To remove the Ghost integration:

1. Go to **Settings → Integrations → Ghost**
2. Click **Disconnect**

This removes your stored credentials from Siftly. The posts already published to Ghost remain untouched — disconnecting does not delete any content.

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Connection fails with 401 Unauthorized">
    Your Admin API key is invalid or has been revoked. Go to Ghost Admin → **Settings → Integrations → Siftly** and verify the Admin API key matches what's entered in Siftly. If in doubt, delete the integration and create a new one to get a fresh key.
  </Accordion>

  <Accordion title="API URL is rejected">
    The API URL must use **HTTPS** and must be publicly reachable. Common mistakes:

    * Using `http://` instead of `https://`
    * Including a trailing slash (Siftly strips it, but double-check)
    * Using an internal or localhost address (blocked for security)
  </Accordion>

  <Accordion title="Admin API key format error">
    The Admin API key must contain exactly one colon separating the key ID and the hex secret. Example format: `69d9c7eeb369ab00013a5f2b:dde97da248911d15686a4d10...`

    Do not use the **Content API key** — that key is read-only and cannot create posts. Use the **Admin API key** only.
  </Accordion>

  <Accordion title="Post publishes but slug conflicts">
    If a post with the same slug already exists in Ghost, the publish will fail with a 422 error. Either delete the existing post in Ghost or adjust the slug in your Siftly content draft before republishing.
  </Accordion>

  <Accordion title="Post content looks different in Ghost">
    Siftly wraps content in Ghost HTML card comments (`<!--kg-card-begin: html-->`) to preserve formatting. If you see raw HTML comments in the editor, Ghost may not have processed the source correctly — ensure your Ghost version is 5.x or later, which has full HTML card support.
  </Accordion>

  <Accordion title="Tags are not appearing on the post">
    Ghost creates tags automatically from the names Siftly sends. If tags aren't showing, check your Ghost Admin → **Tags** to confirm they were created. Ensure the keywords in your Siftly content are not empty.
  </Accordion>
</AccordionGroup>

***

## Security

Siftly takes the following measures to protect your Ghost credentials:

* **Encryption at rest** — the Admin API key is encrypted using Fernet symmetric encryption before being stored in the database. The plaintext key is never persisted.
* **SSRF protection** — the Ghost API URL is validated on both connect and publish. Private IP ranges, localhost, and non-HTTPS URLs are blocked.
* **Short-lived tokens** — JWT tokens are generated fresh on every API call with a 5-minute expiry. No tokens are stored.
* **Never returned to the client** — the Admin API key is never included in any response sent to the browser.
