Categories
Create localized category hierarchies for blog navigation and archives.
Categories organize posts into localized navigation and archive pages. Each category has a stable shared ID, while its name, slug, description, and SEO fields belong to one locale.
Choose a surface
The API, Node SDK, and CLI manage the same category concepts. Compare all Cli Blog tools.
| Action | HTTP API | Node SDK | CLI | Description |
|---|---|---|---|---|
| List | GET /v1/categories | blog.categories.list() or .paginate() | cli-blog categories list | Page through localized categories |
| Create | POST /v1/categories | blog.categories.create() | cli-blog categories create | Create a root, child, or translation |
| Retrieve | GET /v1/categories/{id} | blog.categories.get() | cli-blog categories get | Get a category by shared ID or localized slug |
| Update | POST /v1/categories/{id} | blog.categories.update() | cli-blog categories update | Change localized or hierarchy fields |
| Delete | DELETE /v1/categories/{id} | blog.categories.delete() | cli-blog categories delete | Delete a localized category |
Public and private keys can list and retrieve categories. Create, update, and delete actions require a private key with category permission.
Use the REST API
Call the resource from any technology that can send HTTP requests. Use a public key for allowed delivery reads and a private key for content changes.
GET or POST https://api.cli-blog.com/v1/categoriesChoose an API keyUse the CLI
Use the CLI for local work, continuous integration, and JSON automation. Configure a narrowly scoped key before running content-changing commands.
cli-blog categories list --locale en-US --jsonRead the CLI guideUse the Node SDK
Use the Node SDK in Node.js 20+ servers, scripts, continuous integration, and trusted agent environments. Its methods map to the same resource actions.
await blog.categories.list({ locale: "en-US" })Read the Node SDK guideUse the agent skill
Give the agent the Cli Blog skill and a result-focused instruction. The skill helps it choose the API, CLI, or SDK and keeps publishing behind an approval gate.
Find the Engineering category in en-US. Create it if missing, then return its stable category ID.Install the agent skillCreate a category hierarchy
Create the root first, then pass its shared ID as parent_taxonomy_term_id. Categories support two child levels below a root.
curl "https://api.cli-blog.com/v1/categories" \
--header "x-api-key: $CLI_BLOG_PRIVATE_API_KEY" \
--header "content-type: application/json" \
--data '{
"name": "Engineering",
"slug": "engineering",
"locale": "en-US",
"description": "Architecture, delivery, and developer workflow articles.",
"seo_title": "Engineering articles from Acme"
}'The response returns the shared concept ID assigned to posts:
{
"id": "term_category_917bd2",
"object": "taxonomy_term",
"organization_id": "org_demo_acme",
"taxonomy_type": "category",
"parent_taxonomy_term_id": null,
"locale": "en-US",
"name": "Engineering",
"slug": "engineering",
"description": "Architecture, delivery, and developer workflow articles.",
"seo_title": "Engineering articles from Acme",
"seo_description": null,
"canonical_url": null,
"focus_keyphrase": null,
"seo_keywords": [],
"robots_index": true,
"robots_follow": true,
"open_graph_title": null,
"open_graph_description": null,
"open_graph_media_asset_id": null,
"twitter_title": null,
"twitter_description": null,
"twitter_media_asset_id": null,
"schema_type": "CollectionPage",
"metadata": {},
"created_at": "2026-07-11T17:10:00.000Z",
"updated_at": "2026-07-11T17:10:00.000Z"
}The SDK can create a child category:
import { CliBlog } from "@cli-blog/node";
const blog = new CliBlog({
apiKey: process.env.CLI_BLOG_PRIVATE_API_KEY!,
apiUrl: "https://api.cli-blog.com",
});
const releases = await blog.categories.create({
name: "Release notes",
locale: "en-US",
parent_taxonomy_term_id: "term_category_917bd2",
description: "Product and platform release notes.",
});The CLI exposes the same hierarchy field:
cli-blog categories create \
--name "Release notes" \
--locale en-US \
--parent-taxonomy-term-id term_category_917bd2 \
--description "Product and platform release notes." \
--jsonList and retrieve categories
Cursor pagination is the preferred list mode. Set limit up to 100, then pass next_cursor as after. Use numbered pagination only when a category manager requires exact totals. See Pagination.
Omit locale to use the organization's primary locale. Cli Blog does not fall back to another locale in V1. Add include=translations when the current view needs every localized variant.
const firstPage = await blog.categories.list({
locale: "en-US",
include: "translations",
limit: 50,
});
const category = await blog.categories.get("engineering", {
locale: "en-US",
include: "translations",
});cli-blog categories list \
--locale en-US \
--include translations \
--limit 50 \
--json
cli-blog categories get engineering --locale en-US --jsonAdd a translation
Pass an existing shared category ID as translation_of_id. Post assignments keep using that shared ID across locales.
await blog.categories.create({
name: "Ingeniería",
slug: "ingenieria",
locale: "es-MX",
translation_of_id: "term_category_917bd2",
description: "Arquitectura, entregas y flujos de desarrollo.",
});Update and delete a category
Pass locale when an ID or slug has localized variants. Move a category by updating parent_taxonomy_term_id; the API rejects hierarchies deeper than the supported limit.
await blog.categories.update(
"term_category_917bd2",
{ description: "Engineering systems, practices, and releases." },
{ locale: "en-US" },
);
await blog.categories.delete("term_category_917bd2", { locale: "en-US" });cli-blog categories update term_category_917bd2 \
--locale en-US \
--description "Engineering systems, practices, and releases." \
--json
cli-blog categories delete term_category_917bd2 --locale en-US --yesReview child categories and post assignments before deleting a category.
Category fields
| Field family | Fields and description |
|---|---|
| Identity | id, object, organization_id, and taxonomy_type identify the shared concept |
| Localization | locale, name, slug, description, and optional translations describe localized variants |
| Hierarchy | parent_taxonomy_term_id links a category to its shared parent concept |
| Search and social | Search, canonical, robots, Open Graph, X/Twitter, keyword, and schema_type fields describe the archive page |
| Integration data | metadata stores integration-defined JSON up to 32 KB when serialized |
| Timestamps | created_at and updated_at report ISO timestamps |
Common errors
| Status | Cause | Resolution |
|---|---|---|
400 | Unsupported locale, invalid parent, excessive nesting, or mixed pagination modes | Check the error param and category hierarchy |
401 | Missing or invalid API key | Send the organization key in x-api-key |
403 | Public key used for a write or private key lacks permission | Use a scoped private key |
404 | Shared ID or slug does not exist in the requested locale | Confirm the locale and identifier |
409 | Slug already exists in that locale | Choose a unique locale-scoped slug |
Complete API operations
The operation reference lists every path, locale control, include, request field, response field, hierarchy rule, and error schema. Use Try it to open the request runner.
List categories
List localized categories. Cursor pagination is the default; page and per_page opt into exact numbered pagination and cannot be combined with after or limit. Omitted locale uses the organization primary/default locale; v1 does not fall back across locales.
Authorization
ApiKeyAuth Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.
In: header
Query Parameters
Opaque cursor returned by the previous category list page.
Maximum categories to return. Defaults to 20. The maximum accepted value is 100.
One-based exact page number. Supplying page selects numbered pagination and cannot be combined with after or limit.
Items per numbered page. Requires page, defaults to 20, and cannot be combined with after or limit.
BCP 47 locale tag to list, for example en-US or es-MX. Categories and tags are shared concepts with localized variants; omitted locale uses organization defaults.
Optional related data to include. Use translations to see available localized variants.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/categories"{ "object": "list", "has_more": true, "next_cursor": "string", "page": 0, "per_page": 0, "total_items": 0, "total_pages": 0, "data": [ { "id": "string", "object": "taxonomy_term", "organization_id": "string", "taxonomy_type": "string", "parent_taxonomy_term_id": "string", "locale": "string", "name": "string", "slug": "string", "description": "string", "seo_title": "string", "seo_description": "string", "canonical_url": "string", "focus_keyphrase": "string", "seo_keywords": [ "string" ], "robots_index": true, "robots_follow": true, "open_graph_title": "string", "open_graph_description": "string", "open_graph_media_asset_id": "string", "twitter_title": "string", "twitter_description": "string", "twitter_media_asset_id": "string", "schema_type": "string", "metadata": null, "created_at": "string", "updated_at": "string", "translations": [ { "id": "string", "locale": "string", "name": "string", "slug": "string" } ] } ]}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}Create a category
Send a JSON body to create a localized category. Its stable shared ID can be assigned to posts across locales. Omit locale to use the organization primary/default locale.
Authorization
ApiKeyAuth Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/categories" \ -H "Content-Type: application/json" \ -d '{ "name": "string" }'{ "id": "string", "object": "taxonomy_term", "organization_id": "string", "taxonomy_type": "string", "parent_taxonomy_term_id": "string", "locale": "string", "name": "string", "slug": "string", "description": "string", "seo_title": "string", "seo_description": "string", "canonical_url": "string", "focus_keyphrase": "string", "seo_keywords": [ "string" ], "robots_index": true, "robots_follow": true, "open_graph_title": "string", "open_graph_description": "string", "open_graph_media_asset_id": "string", "twitter_title": "string", "twitter_description": "string", "twitter_media_asset_id": "string", "schema_type": "string", "metadata": null, "created_at": "string", "updated_at": "string", "translations": [ { "id": "string", "locale": "string", "name": "string", "slug": "string" } ]}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}Retrieve a category
Retrieve a category by its stable shared ID or locale-scoped slug. Omitted locale uses the organization primary/default locale; v1 does not automatically fall back to another locale.
Authorization
ApiKeyAuth Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.
In: header
Path Parameters
Shared category concept ID or locale-scoped slug.
Query Parameters
BCP 47 locale tag used for category slug lookup.
Optional related data to include. Use translations to see available localized variants.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/categories/string"{ "id": "string", "object": "taxonomy_term", "organization_id": "string", "taxonomy_type": "string", "parent_taxonomy_term_id": "string", "locale": "string", "name": "string", "slug": "string", "description": "string", "seo_title": "string", "seo_description": "string", "canonical_url": "string", "focus_keyphrase": "string", "seo_keywords": [ "string" ], "robots_index": true, "robots_follow": true, "open_graph_title": "string", "open_graph_description": "string", "open_graph_media_asset_id": "string", "twitter_title": "string", "twitter_description": "string", "twitter_media_asset_id": "string", "schema_type": "string", "metadata": null, "created_at": "string", "updated_at": "string", "translations": [ { "id": "string", "locale": "string", "name": "string", "slug": "string" } ]}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}Update a category
Send a JSON body to update a localized category or its category parent.
Authorization
ApiKeyAuth Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.
In: header
Path Parameters
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/categories/string" \ -H "Content-Type: application/json" \ -d '{}'{ "id": "string", "object": "taxonomy_term", "organization_id": "string", "taxonomy_type": "string", "parent_taxonomy_term_id": "string", "locale": "string", "name": "string", "slug": "string", "description": "string", "seo_title": "string", "seo_description": "string", "canonical_url": "string", "focus_keyphrase": "string", "seo_keywords": [ "string" ], "robots_index": true, "robots_follow": true, "open_graph_title": "string", "open_graph_description": "string", "open_graph_media_asset_id": "string", "twitter_title": "string", "twitter_description": "string", "twitter_media_asset_id": "string", "schema_type": "string", "metadata": null, "created_at": "string", "updated_at": "string", "translations": [ { "id": "string", "locale": "string", "name": "string", "slug": "string" } ]}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}Delete a category
Delete a localized category.
Authorization
ApiKeyAuth Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.
In: header
Path Parameters
Query Parameters
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/categories/string"{ "deleted": true, "id": "string"}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}{ "error": { "code": "invalid_request", "message": "string", "param": "string" }}