$ cli-blog docs

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.

ActionHTTP APINode SDKCLIDescription
ListGET /v1/categoriesblog.categories.list() or .paginate()cli-blog categories listPage through localized categories
CreatePOST /v1/categoriesblog.categories.create()cli-blog categories createCreate a root, child, or translation
RetrieveGET /v1/categories/{id}blog.categories.get()cli-blog categories getGet a category by shared ID or localized slug
UpdatePOST /v1/categories/{id}blog.categories.update()cli-blog categories updateChange localized or hierarchy fields
DeleteDELETE /v1/categories/{id}blog.categories.delete()cli-blog categories deleteDelete 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/categories
Choose an API key

Use 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 --json
Read the CLI guide

Use 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 guide

Use 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 skill

Create 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." \
  --json

List 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 --json

Add 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 --yes

Review child categories and post assignments before deleting a category.

Category fields

Field familyFields and description
Identityid, object, organization_id, and taxonomy_type identify the shared concept
Localizationlocale, name, slug, description, and optional translations describe localized variants
Hierarchyparent_taxonomy_term_id links a category to its shared parent concept
Search and socialSearch, canonical, robots, Open Graph, X/Twitter, keyword, and schema_type fields describe the archive page
Integration datametadata stores integration-defined JSON up to 32 KB when serialized
Timestampscreated_at and updated_at report ISO timestamps

Common errors

StatusCauseResolution
400Unsupported locale, invalid parent, excessive nesting, or mixed pagination modesCheck the error param and category hierarchy
401Missing or invalid API keySend the organization key in x-api-key
403Public key used for a write or private key lacks permissionUse a scoped private key
404Shared ID or slug does not exist in the requested localeConfirm the locale and identifier
409Slug already exists in that localeChoose 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

Key type: Public or private key

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
x-api-key<token>

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

after?string

Opaque cursor returned by the previous category list page.

limit?|

Maximum categories to return. Defaults to 20. The maximum accepted value is 100.

page?|

One-based exact page number. Supplying page selects numbered pagination and cannot be combined with after or limit.

per_page?|

Items per numbered page. Requires page, defaults to 20, and cannot be combined with after or limit.

locale?string

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.

include?array<string>

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

Key type: Private key

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
x-api-key<token>

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

Key type: Public or private key

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
x-api-key<token>

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

id*string

Shared category concept ID or locale-scoped slug.

Query Parameters

locale?string

BCP 47 locale tag used for category slug lookup.

include?array<string>

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

Key type: Private key

Send a JSON body to update a localized category or its category parent.

Authorization

ApiKeyAuth
x-api-key<token>

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

id*string

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

Key type: Private key

Delete a localized category.

Authorization

ApiKeyAuth
x-api-key<token>

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

id*string

Query Parameters

locale?string

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"  }}

On this page