6.4 KiB
6.4 KiB
Ghost Content API Authentication Documentation
Overview
Ghost Content API provides read-only access to published content using simple query parameter authentication. It's designed for public consumption and is safe for client-side use.
Authentication Method
API Key Format
- Type: Content API Key (different from Admin API Key)
- Source: Ghost Admin → Settings → Integrations → Custom Integration
- Security: Safe for browser/public environments
- Access: Read-only to published content only
Request Authentication
Query Parameter Method
GET /ghost/api/content/posts/?key={content_api_key}
Required Headers
Accept-Version: v5.0
Example Request
GET /ghost/api/content/posts/?key=22444f78cc7a3e8d0b5eaa18&limit=5 HTTP/1.1
Host: localhost:2368
Accept-Version: v5.0
Implementation for MCP Server
Node.js Implementation
Simple Request Function
class GhostContentAuth {
constructor(apiKey, ghostUrl = 'http://localhost:2368') {
this.apiKey = apiKey;
this.baseUrl = `${ghostUrl}/ghost/api/content`;
}
buildUrl(endpoint, params = {}) {
const url = new URL(`${this.baseUrl}${endpoint}`);
// Add API key
url.searchParams.set('key', this.apiKey);
// Add additional parameters
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.set(key, value);
}
});
return url.toString();
}
getHeaders() {
return {
'Accept-Version': 'v5.0',
'Accept': 'application/json'
};
}
async request(endpoint, params = {}) {
const url = this.buildUrl(endpoint, params);
const response = await fetch(url, {
headers: this.getHeaders()
});
if (!response.ok) {
throw new Error(`Content API request failed: ${response.status}`);
}
return response.json();
}
}
Usage Examples
const contentAuth = new GhostContentAuth('your_content_api_key');
// Get all posts
const posts = await contentAuth.request('/posts/', {
limit: 10,
include: 'tags,authors'
});
// Get specific post by slug
const post = await contentAuth.request('/posts/slug/welcome/', {
include: 'tags,authors'
});
// Get site settings
const settings = await contentAuth.request('/settings/');
Available Endpoints
Core Resources
GET /posts/- All published postsGET /posts/{id}/- Specific post by IDGET /posts/slug/{slug}/- Specific post by slugGET /pages/- All published pagesGET /pages/{id}/- Specific page by IDGET /pages/slug/{slug}/- Specific page by slugGET /tags/- All public tagsGET /tags/{id}/- Specific tag by IDGET /tags/slug/{slug}/- Specific tag by slugGET /authors/- All authorsGET /authors/{id}/- Specific author by IDGET /authors/slug/{slug}/- Specific author by slugGET /tiers/- Membership tiersGET /settings/- Public site settings
Query Parameters
Common Parameters
key(required): Content API keylimit: Number of resources (default: 15, max: 50)page: Page number for paginationfields: Comma-separated list of fields to includeinclude: Related resources to include (e.g., 'tags,authors')filter: Filter resources using Ghost's filter syntax
Filter Examples
// Featured posts only
const featured = await contentAuth.request('/posts/', {
filter: 'featured:true'
});
// Posts with specific tag
const newsPosts = await contentAuth.request('/posts/', {
filter: 'tag:news'
});
// Posts by specific author
const authorPosts = await contentAuth.request('/posts/', {
filter: 'author:john-doe'
});
Response Format
Standard Response Structure
{
"posts": [
{
"id": "post_id",
"title": "Post Title",
"slug": "post-slug",
"html": "<p>Post content...</p>",
"feature_image": "image_url",
"published_at": "2023-01-01T00:00:00.000Z",
"created_at": "2023-01-01T00:00:00.000Z",
"updated_at": "2023-01-01T00:00:00.000Z",
"tags": [...],
"authors": [...]
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 15,
"pages": 1,
"total": 1,
"next": null,
"prev": null
}
}
}
Error Handling
Missing API Key
Request: Without key parameter
Response: 401 Unauthorized
{
"errors": [
{
"message": "Authorization failed",
"context": "Unable to determine the authenticated member or integration. Check the supplied Content API Key...",
"type": "UnauthorizedError"
}
]
}
Invalid API Key
Response: 401 Unauthorized Action: Verify key is correct and active
Resource Not Found
Response: 404 Not Found
{
"errors": [
{
"message": "Resource not found",
"type": "NotFoundError"
}
]
}
Security Considerations
Safe for Public Use
- Content API keys only access published content
- No sensitive data exposure
- Can be used in browsers, mobile apps, etc.
Private Sites
- Be cautious with key distribution on private Ghost sites
- Consider access restrictions if content should be limited
Key Management
- Keys can be regenerated in Ghost Admin
- Multiple integrations can have different keys
- Monitor key usage if needed
Rate Limiting & Caching
Caching Behavior
- Content API responses are fully cacheable
- Cache headers provided in responses
- Recommended to implement caching in client
Rate Limiting
- No strict rate limits documented
- Reasonable usage expected
- Monitor response times and adjust accordingly
Testing Strategy
Manual Testing
# Test with curl
curl "http://localhost:2368/ghost/api/content/posts/?key=YOUR_KEY&limit=1" \
-H "Accept-Version: v5.0"
Automated Testing
// Test basic authentication
test('Content API authentication', async () => {
const response = await contentAuth.request('/settings/');
expect(response.settings).toBeDefined();
});
// Test error handling
test('Invalid key handling', async () => {
const invalidAuth = new GhostContentAuth('invalid_key');
await expect(invalidAuth.request('/posts/')).rejects.toThrow();
});
Implementation Status
- ✅ Authentication method documented
- ✅ Request format identified
- ✅ Error handling patterns documented
- ✅ Node.js implementation designed
- ⏳ Implementation pending API key generation
- ⏳ Testing pending API key availability