The Help.Center API lets you programmatically manage your help center articles. Create, update, publish, organize articles, and manage categories without ever opening the dashboard.
Tip: Want to skip the manual API calls? Install the Help.Center Agent Skill to manage articles using plain language with AI coding agents like Claude Code, Cursor, or OpenClaw.
Before You Start
You'll need to create an API key from your Help.Center dashboard:
Go to Settings > General > API
Click "Create API Key"
Enter a name for your key (e.g., "Production", "Development")
Select the scopes you need based on what operations you'll perform
Copy your API Key and save it securely - you won't see it again!
Note your Center ID from the API page
All API requests go to https://api.help.center and require your API key in the header:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonAPI Key Scopes
API keys use a scope-based permission system. When creating a key, select only the scopes you need:
Scope | Allows | Required Dashboard Permission |
|---|---|---|
| View articles, drafts, categories, and counts | Content Read |
| Create and edit articles, drafts, metadata, categories, upload images | Content Edit + Update |
| Publish, unpublish, and duplicate articles | Content Publish |
| Delete articles and categories | Content Delete |
Security Tip: Create multiple API keys with minimal scopes for different purposes. For example, use a read-only key for analytics and a separate key with write permissions for content management.
Quick Start: Create Your First Article
Here's how to create and publish an article in two steps:
Step 1: Create a Draft
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Getting Started with Our Product",
"content": {
"html": "<h1>Welcome</h1><p>Your article content here...</p>"
},
"category_id": "getting-started"
}' \
"https://api.help.center/v0/centers/YOUR_CENTER_ID/articles"Step 2: Publish It
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
"https://api.help.center/v0/centers/YOUR_CENTER_ID/articles/ARTICLE_ID/publish"That's it! Your article is now live. Note that all articles are created as drafts first and must be explicitly published.
API Endpoints
Center & Categories
Action | Method | Endpoint | Required Scope |
|---|---|---|---|
Get center info | GET |
|
|
List categories | GET |
|
|
Create category | POST |
|
|
Update category | PATCH |
|
|
Delete category | DELETE |
|
|
Articles
Action | Method | Endpoint | Required Scope |
|---|---|---|---|
List articles | GET |
|
|
Search articles | GET |
|
|
Get article | GET |
|
|
Count articles | GET |
|
|
Create article | POST |
|
|
Update metadata | PATCH |
|
|
Delete article | DELETE |
|
|
Publish | POST |
|
|
Unpublish | POST |
|
|
Duplicate | POST |
|
|
Drafts
Action | Method | Endpoint | Required Scope |
|---|---|---|---|
List drafts | GET |
|
|
Count drafts | GET |
|
|
Get draft | GET |
|
|
Update draft | PATCH |
|
|
Discard draft | POST |
|
|
Translations
Action | Method | Endpoint | Required Scope |
|---|---|---|---|
Get translation | GET |
|
|
Get draft translation | GET |
|
|
Update translation draft | PATCH |
|
|
Update translation metadata | PATCH |
|
|
Delete translation | DELETE |
|
|
Publish translation | POST |
|
|
Unpublish translation | POST |
|
|
Images
Action | Method | Endpoint | Required Scope |
|---|---|---|---|
Upload image | POST |
|
|
Upload for article | POST |
|
|
Category Management
Categories help organize your articles. You can create hierarchical categories with one level of subcategories.
Creating Categories
POST /v0/centers/:centerId/articles/categoriesRequest body:
{
"name": "Getting Started",
"description": "Articles for new users",
"icon": "<svg>...</svg>", // Optional custom SVG icon
"parent_id": "parent-cat-id" // Optional, for subcategories
}Categories automatically get a URL-friendly slug and unique ID. If no icon is provided, a default book icon is used.
Updating Categories
PATCH /v0/centers/:centerId/articles/categories/:categoryId{
"name": "Updated Name",
"description": "Updated description",
"icon": "<svg>...</svg>"
}Deleting Categories
Categories can only be deleted if no articles are using them:
DELETE /v0/centers/:centerId/articles/categories/:categoryIdIf articles exist in the category, you'll get an error with the list of article IDs that need to be moved or deleted first.
Image Upload
Upload images for use in your articles:
POST /v0/centers/:centerId/articles/imagesRequest: Use multipart/form-data with field name image
Maximum size: 10MB
Supported formats: JPEG, PNG, GIF, WebP, SVG
Response:
{
"success": true,
"data": {
"url": "https://cdn.help.center/images/...",
"filename": "image.png",
"size": 1024576
}
}Listing Articles
Fetch your published articles with optional filters:
GET /v0/centers/:centerId/articlesQuery parameters:
limit- Number of results (default: 50, max: 100)starting_after- Cursor for forward pagination (pass the last article's ID)ending_before- Cursor for backward paginationcategory- Filter by category IDsearch- Search by title or contentexpand[]=content- Include full HTML and text content in the response
Response includes new fields:
{
"id": "article-id",
"url": "https://support.help.center/article/1091-getting-started",
"preview_url": "https://support.help.center/article/preview-abc123",
// ... other fields
}Creating Articles
POST /v0/centers/:centerId/articlesRequest body:
{
"title": "Your Article Title",
"content": {
"html": "<h1>Title</h1><p>Your content here</p>"
},
"category_id": "getting-started",
"metadata": {
"seo": {
"title": "SEO-friendly title (50-60 chars)",
"description": "Meta description (150-160 chars)"
}
}
}Important: Articles are always created as drafts. You must call the publish endpoint separately to make them live. HTML content is automatically sanitized for security.
Updating Articles
To update an article's content, use the draft endpoint:
PATCH /v0/centers/:centerId/articles/:articleId/draft{
"title": "Updated Title",
"html": "<h1>Updated Content</h1><p>New content here...</p>"
}To update metadata (category, slug, SEO) without touching content:
PATCH /v0/centers/:centerId/articles/:articleId/metadata{
"category": "new-category-id",
"slug": "new-article-slug",
"seo": {
"title": "New SEO Title",
"description": "New SEO Description"
}
}Publishing & Unpublishing
Publish a draft to make it live:
POST /v0/centers/:centerId/articles/:articleId/publishUnpublish to revert to draft:
POST /v0/centers/:centerId/articles/:articleId/unpublishDraft Management
Every article has a draft version. When you edit a published article, changes stay in draft until you publish again.
Draft statuses:
unpublished- Never been publishedpublished- In sync with the live versionpublished_with_changes- Has unpublished edits
List all drafts:
GET /v0/centers/:centerId/articles/draftsSupports the same limit, starting_after, category, search, and expand[] parameters, plus a status filter.
To discard unpublished changes and restore the published version:
POST /v0/centers/:centerId/articles/:articleId/draft/discardArticle Translations
Manage multilingual content by creating translations of your articles. Each translation maintains its own draft and published state.
Checking Available Languages
Get the center info to see which languages are configured:
GET /v0/centers/:centerIdResponse includes:
{
"id": "center-id",
"name": "Support Center",
"domain": "support.help.center",
"default_language": "en",
"additional_languages": ["es", "fr", "de", "ja"],
"created_at": "2024-01-01T00:00:00.000Z"
}Use the language codes from additional_languages in translation endpoints. Standard ISO 639-1 codes like es (Spanish), fr (French), de (German), ja (Japanese), etc.
Article Response with Translations
When fetching articles, the response includes available translations:
GET /v0/centers/:centerId/articles/:articleId{
"id": "article-id",
"title": "Getting Started",
"translations": ["es", "fr"], // Languages with published translations
// ... other fields
}Creating/Updating Translations
To create or update a translation, use the draft endpoint with the language code:
PATCH /v0/centers/:centerId/articles/:articleId/translations/es/draft{
"title": "Título del artículo",
"html": "<h1>Contenido en español</h1><p>...</p>"
}Important: The language must be in the center's additional_languages array, or you'll get an error.
Publishing Translations
Translations are managed independently from the main article:
POST /v0/centers/:centerId/articles/:articleId/translations/es/publishNote: The main article must be published before you can publish any translations.
Fetching Translations
Get a specific translation of an article:
GET /v0/centers/:centerId/articles/:articleId/translations/es?expand[]=contentResponse:
{
"id": "article-id",
"language": "es",
"title": "Título del artículo",
"status": "published",
"url": "https://support.help.center/es/article/1091-articulo",
"content": {
"html": "...",
"text": "..."
}
}Translation Workflow
Check available languages - Use GET center info to see
additional_languagesCreate/Update Draft - Use PATCH on the translation draft endpoint
Update Metadata - Optionally update SEO and slug for the translation
Publish - Make the translation live (main article must be published first)
Manage Independently - Each translation can be published/unpublished separately
Translation Metadata
Update translation-specific metadata separately from content:
PATCH /v0/centers/:centerId/articles/:articleId/translations/es/metadata{
"slug": "empezando",
"seo": {
"metaTitle": "Título SEO",
"metaDesc": "Descripción SEO"
}
}Important Notes
Translations are tied to the main article - deleting the main article removes all translations
Each translation has its own draft and published state
Translation URLs follow the pattern:
https://[domain]/[language]/article/[slugId]-[slug]HTML content is automatically sanitized for all translations
When unpublishing the main article, all translations are also unpublished
Embeddings are created separately for each language to enable language-specific search
Managing API Keys
Best practices for API key management:
Create multiple keys - Use different keys for different environments (production, staging, development)
Use minimal scopes - Only grant the permissions each key needs
Name keys clearly - Use descriptive names like "Production Read-Only" or "CI/CD Publisher"
Rotate regularly - Periodically create new keys and revoke old ones
Monitor usage - Check your dashboard to see which keys are being used
You can create, update, and revoke keys from the dashboard at any time without affecting other keys.
Pagination
The API uses cursor-based pagination. Use starting_after with the last item's ID to get the next page:
GET /v0/centers/:centerId/articles?starting_after=last_article_id&limit=10Check the has_more field in the response to know if more pages exist.
Rate Limiting
100 requests per minute per API key (burst up to 200)
Check response headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-ResetIf you hit the limit, wait until the reset time before retrying
Error Handling
The API returns standard HTTP status codes with descriptive error messages:
400 - Bad request (missing fields, invalid parameters)
401 - Invalid or missing API key
403 - Insufficient permissions (missing required scope)
404 - Article, category, or center not found
429 - Rate limited
500 - Server error
Error responses include a type, message, and code to help you debug:
{
"error": {
"type": "invalid_request_error",
"message": "You do not have the 'content.publish' scope",
"code": "insufficient_scope"
}
}Code Examples
JavaScript / Node.js
const axios = require('axios');
const api = axios.create({
baseURL: 'https://api.help.center',
headers: {
'Authorization': `Bearer ${process.env.HC_API_KEY}`,
'Content-Type': 'application/json'
}
});
// List articles
const { data } = await api.get(
`/v0/centers/${centerId}/articles`,
{ params: { category: 'getting-started', limit: 10 } }
);
// Create and publish an article
const article = await api.post(`/v0/centers/${centerId}/articles`, {
title: 'New Article',
content: { html: '<p>Content</p>' },
category_id: 'guides'
});
await api.post(`/v0/centers/${centerId}/articles/${article.data.id}/publish`);Python
import requests
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
# List articles
response = requests.get(
f'https://api.help.center/v0/centers/{center_id}/articles',
headers=headers,
params={'category': 'getting-started', 'limit': 10}
)
articles = response.json()
# Upload an image
with open('image.png', 'rb') as f:
files = {'image': f}
response = requests.post(
f'https://api.help.center/v0/centers/{center_id}/articles/images',
headers={'Authorization': f'Bearer {api_key}'},
files=files
)
image_url = response.json()['data']['url']Best Practices
Always use HTTPS - All API requests must use HTTPS
Keep your API keys secret - Never expose them in client-side code or public repositories
Use appropriate scopes - Only request the permissions you need
Use pagination - Don't try to fetch all articles at once
Use
expand[]wisely - Only request content when you need it to reduce bandwidthHandle errors gracefully - Check status codes and parse error responses
Respect rate limits - Implement exponential backoff when you hit limits
Sanitize content - Although the API sanitizes HTML, validate user input on your end too
Still need help?
Contact us