Content API Ghost
Content API Ghost : Guide Complet avec Exemples JavaScript
La Content API de Ghost est l'outil principal pour récupérer le contenu de votre site de manière programmatique. Elle est conçue pour les setups headless, où Ghost gère le backend (contenu, membres, newsletters) et un frontend séparé (comme Next.js, Gatsby ou une app mobile) affiche les données. En 2025, avec la version v5 (et des mises à jour vers v6 pour la compatibilité), elle reste simple, rapide et focalisée sur la lecture seule. Pas de modifications possibles ici – pour ça, utilisez l'Admin API.
Ce guide s'adresse aux débutants en développement, aux petites entreprises ou startups qui veulent découpler leur site pour plus de flexibilité. On va couvrir l'essentiel : authentification, ressources disponibles, paramètres pour filtrer et paginer, et des exemples concrets en JavaScript. Tout est basé sur la doc officielle Ghost, sans fioritures.
Pourquoi utiliser la Content API ?
Dans un monde où les sites doivent être rapides et personnalisables, Ghost en headless CMS brille. Vous publiez du contenu via l'admin Ghost, et la Content API le sert en JSON pur. Avantages pour une SMB ou startup :
- Performance : Pas de rendu serveur lourd, idéal pour des apps React ou Vue.
- Évolutivité : Intégrez avec n'importe quel framework sans toucher au thème Ghost.
- Sécurité : Lecture publique seulement, pas d'accès admin exposé.
- Cas d'usage : Blog découplé, app mobile pour vos newsletters, ou site e-commerce basique avec tiers payants.
Limites : Seulement les contenus publics (posts publiés, pages visibles). Pour du contenu protégé (membres), ajoutez une couche d'authentification frontend.
Authentification : Obtenez votre clé publique
Pour accéder à l'API, créez une intégration dans l'admin Ghost :
- Allez dans Intégrations > Ajouter une intégration personnalisée.
- Nommez-la (ex. : "Mon frontend headless").
- Copiez la clé Content API (un string hex comme
22444f78447824223cefc48062).
L'URL de base est https://votre-site.com/ghost/api/content/. Ajoutez ?key=VOTRE_CLE à chaque requête GET.
Pour les sites avec membres, utilisez un browse token (JWT) pour des accès limités, mais pour débuter, la clé publique suffit. Versionnez avec ?version=v5.0 (ou v6.0 pour les futures compatibilités).
Exemple basique en curl :
curl "https://demo.ghost.io/ghost/api/content/posts/?key=22444f78447824223cefc48062"
Ça retourne du JSON avec une liste de posts. En JS, on utilise fetch ou le SDK officiel.
Les ressources disponibles : Ce que vous pouvez récupérer
La Content API expose plusieurs endpoints en lecture seule. Tous en GET, avec pagination par défaut (15 items).
Posts
Les articles principaux. Endpoint : /content/posts/.
- Browse : Liste tous les posts publiés (triés par
published_atDESC, le plus récent en premier). - Read : Un post par ID (
/posts/{id}/) ou slug (/posts/slug/{slug}/).
Paramètres spécifiques : Filtrez par statut (published), incluez auteurs/tags.
Réponse : Array posts avec id, slug, title, html, feature_image, excerpt, url, created_at, etc. Sans include, pas d'auteurs ou tags.
Exemple réponse (simplifié) :
{
"posts": [
{
"id": "5c7ece47da174000c0c5c6d7",
"slug": "welcome-short",
"title": "Welcome",
"html": "<p>Bienvenue sur Ghost.</p>",
"feature_image": "https://example.com/image.jpg",
"url": "https://votre-site.com/welcome-short/",
"excerpt": "Un extrait court.",
"published_at": "2025-01-01T10:00:00.000Z"
}
]
}
Pages
Comme les posts, mais pour du contenu statique (ex. : À propos). Endpoint : /content/pages/.
- Tri par défaut :
titleASC (alphabétique). - Même structure que posts, mais clé
pagesdans la réponse.
Utilisez pour des landing pages custom dans votre frontend.
Tags
Catégories pour organiser le contenu. Endpoint : /content/tags/.
- Browse : Liste triée par
nameASC. - Read : Par ID ou slug.
- Incluez
count.postspour le nombre d'articles par tag.
Réponse : Array tags avec id, name, slug, description, visibility (public/internal), url.
Exemple : Filtrez les posts d'un tag avec filter=tag:mon-tag sur l'endpoint posts.
Authors
Les contributeurs. Endpoint : /content/authors/.
- Browse : Tri par
nameASC. Seulement les auteurs avec au moins un post. - Read : Par ID ou slug.
- Incluez
count.postspour le nombre de publications.
Réponse : Array authors avec id, name, slug, bio, profile_image, url, website, twitter, etc.
Idéal pour des pages auteur dynamiques.
Tiers
Pour les sites avec memberships. Endpoint : /content/tiers/.
- Liste triée par prix mensuel croissant.
- Incluez
monthly_price,yearly_price,benefitspour les détails payants.
Réponse : Array tiers avec id, name, slug, description, active, type (free/paid), currency, welcome_page_url, timestamps.
Utilisez pour afficher les abonnements sur votre portail custom.
Settings
Configuration du site. Endpoint : /content/settings/.
- Retourne un objet unique avec
title,description,logo,icon,navigation(array de liens),meta_title,og_image, etc. Pas de paramètres.
Parfait pour fetcher le menu ou le logo une seule fois au chargement de l'app.
Paramètres essentiels : Filtrage, pagination et plus
Tous les endpoints "browse" acceptent ces query params. Ils sont URL-encodés automatiquement en JS.
Filtrage (filter)
Utilise NQL (Ghost's query language). Syntaxe : propriété:opérateur valeur. Opérateurs : =, !=, >, <, in, ~ (contient), + (AND), , (OR), - (NOT), parenthèses pour grouper.
- Propriétés : Champs comme
title,slug, ou inclus commeauthors.slug. - Valeurs : Strings (sans quotes sauf pour dates/cols), nombres,
null,true/false, dates relatives (now-30d).
Exemples pour posts :
filter=tag:tech: Posts avec tag "tech".filter=author:john+published_at>2025-01-01: Posts de John après janvier 2025.filter=title:~ghost: Titres contenant "ghost".filter=(tag:news+tag:-premium): News mais pas premium.
Pour tags : filter=visibility:public.
Pour authors : filter=name:john.
Pas de partial matches (pas de LIKE), et encodé URL (ex. : %20 pour espaces).
Pagination
Par défaut : 15 items/page. Utilisez limit (1-100) et page (1-based).
- Réponse inclut
meta.pagination:{page: 1, limit: 15, pages: 5, total: 75, next: 2, prev: null}.
Cursor-based pour de gros volumes (avancé, pas détaillé ici).
Exemple : ?limit=5&page=2.
Autres paramètres
- Fields : Limitez les champs retournés, ex.
?fields=title,slug,urlpour optimiser (mais évitez avecinclude). - Include : Ajoutez relations. Ex.
?include=authors,tagspour posts (ajoute arraysauthors,tags, etprimary_author/tag). Pour tiers :?include=monthly_price,benefits. - Order : Tri custom, ex.
?order=published_at asc(SQL-like). - Formats (posts/pages) :
?formats=html,plaintextpour du texte brut en plus du HTML.
Le SDK JavaScript : Simplifiez vos requêtes
Ghost fournit un client JS officiel (@tryghost/content-api) pour éviter les fetch manuels. Installez via npm : npm install @tryghost/content-api.
Initialisation :
import GhostContentAPI from '@tryghost/content-api';
const api = new GhostContentAPI({
url: 'https://votre-site.com', // Sans slash final
key: 'votre-cle-hex',
version: 'v5.0' // Ou v6.0
});
Méthodes : browse({options}) pour listes (Promise avec meta), read({id ou slug}, options) pour single.
Exemples pour fetcher des posts
// Liste de 5 posts avec auteurs et tags
api.posts.browse({ limit: 5, include: 'tags,authors' })
.then(posts => {
posts.forEach(post => {
console.log(`${post.title} par ${post.primary_author.name}`);
});
})
.catch(err => console.error('Erreur:', err));
// Post par slug, avec plaintext
api.posts.read({ slug: 'mon-article' }, { formats: ['html', 'plaintext'] })
.then(post => console.log(post.plaintext))
.catch(err => console.error(err));
Filtrage et pagination en SDK
// Posts d'un tag, page 2
api.posts.browse({
filter: 'tag:tech',
limit: 10,
page: 2,
include: 'authors'
}).then(posts => {
console.log(`Page ${posts.meta.pagination.page} de ${posts.meta.pagination.pages}`);
});
Pour authors et tags
// Auteurs avec count posts
api.authors.browse({ include: 'count.posts' })
.then(authors => authors.forEach(author => console.log(`${author.name}: ${author.count.posts} posts`)));
// Tag par slug
api.tags.read({ slug: 'news' }, { include: 'count.posts' });
Settings
api.settings.browse().then(settings => {
console.log(`Titre du site: ${settings.title}`);
console.log('Navigation:', settings.navigation);
});
Pour tiers :
api.tiers.browse({ include: 'monthly_price,benefits' })
.then(tiers => tiers.forEach(tier => console.log(`${tier.name}: ${tier.monthly_price} €/mois`)));
Le SDK gère l'encodage et les erreurs automatiquement.
Gestion des erreurs
Réponses en JSON avec errors array : {message: "Détail", errorType: "Type"}.
- 400 Bad Request : Filtre mal formé (ex. non encodé).
- 401 Unauthorized : Clé invalide.
- 404 Not Found : Ressource inexistante (ex. post draft).
- 500 Internal : Erreur serveur.
En JS :
.catch(err => {
if (err.status === 400) console.log('Vérifiez votre filtre');
else console.error(err.message);
});
Filtre sans match ? Array vide, pas d'erreur.
Exemples pratiques pour un frontend headless
1. Liste de blog avec Next.js
Dans pages/index.js :
import GhostContentAPI from '@tryghost/content-api';
const api = new GhostContentAPI({ /* config */ });
export async function getStaticProps() {
const posts = await api.posts.browse({
limit: 10,
include: 'tags',
filter: 'featured:false' // Exclut les featured si besoin
});
return { props: { posts } };
}
export default function Home({ posts }) {
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2><a href={post.url}>{post.title}</a></h2>
<p>{post.excerpt}</p>
<ul>{post.tags.map(tag => <li key={tag.id}>{tag.name}</li>)}</ul>
</article>
))}
</div>
);
}
2. Page auteur dynamique
Pour /author/[slug].js :
export async function getStaticPaths() {
const authors = await api.authors.browse();
return {
paths: authors.map(author => ({ params: { slug: author.slug } })),
fallback: false
};
}
export async function getStaticProps({ params }) {
const author = await api.authors.read({ slug: params.slug }, { include: 'count.posts' });
const posts = await api.posts.browse({ filter: `authors:${author.slug}`, limit: 5 });
return { props: { author, posts } };
}
3. App mobile avec React Native (ex. pour newsletters)
Utilisez le SDK dans un hook :
import { useState, useEffect } from 'react';
function PostsList() {
const [posts, setPosts] = useState([]);
useEffect(() => {
api.posts.browse({ limit: 20, include: 'authors' })
.then(setPosts)
.catch(console.error);
}, []);
return posts.map(post => <Text key={post.id}>{post.title}</Text>);
}
Pour tiers dans une app Stripe-integrated : Fetch tiers et affichez les prix/bénéfices.
4. Pagination infinie avec Gatsby
Dans un query GraphQL (Gatsby source-ghost) ou fetch manuel avec page incrementé onScroll.
Bonnes pratiques pour 2026
- Cachez : Utilisez Redis ou Vercel Edge pour éviter les appels répétés.
- Optimisez :
fieldspour payloads légers,includeseulement si besoin. - Versionnez : Toujours spécifiez
v5.0pour éviter les breaks. - Tests : Utilisez demo.ghost.io pour prototyper.
- Scalabilité : Pour startups, combinez avec webhooks pour rebuilds automatiques (ex. sur publication).
En résumé, la Content API rend Ghost ultra-flexible pour des frontends modernes. Commencez par installer le SDK, fetcher des posts basiques, et itérez. Si vous monétisez, intégrez tiers tôt. Pour des tutos plus avancés, checkez la doc officielle.