Categories

Categories let you organise posts into thematic groups with a hierarchical structure. A category can contain subcategories, and those subcategories can contain further subcategories. Use them to build blog navigation menus, category pages with filtered posts, or thematic sections on your site. The API returns the full tree in a single request.

GET /categories — Category tree

Returns all categories for a website organised as a tree. There is no pagination — the response always includes the full tree. This is intentional: category trees are typically small (at most a few dozen nodes) and having them all at once greatly simplifies the frontend logic for building menus and navigation. Root categories have parent_id: null; their children are nested under the children field.

Parameter Type Required Description
web_id string Yes Website ID
lang string No Language code
curl "https://your-panel.com/api/v1/categories?web_id=abc-123&lang=en" \
  -H "X-API-Key: your-api-key"
Expected response 200 OK
{
  "data": [
    {
      "id": "cat-001",
      "nombre": "Technology",
      "slug": "technology",
      "description": null,
      "parent_id": null,
      "children": [
        {
          "id": "cat-002",
          "nombre": "APIs",
          "slug": "apis",
          "description": "Everything about REST and GraphQL APIs",
          "parent_id": "cat-001",
          "children": []
        }
      ]
    },
    {
      "id": "cat-003",
      "nombre": "Design",
      "slug": "design",
      "description": null,
      "parent_id": null,
      "children": []
    }
  ]
}

Response fields

Field Type Description
idUUIDUnique identifier
nombrestringCategory name
slugstringURL-friendly string
descriptionstring | nullOptional description
parent_idUUID | nullParent ID. null if root category
childrenarrayNested child categories (same structure, recursive)

The children structure is recursive: each element has exactly the same fields as the parent category, including its own children. To traverse the full tree use a recursive function:

// Flatten the tree into a plain array of categories
function flattenCategories(categories, depth = 0) {
  return categories.reduce((acc, cat) => {
    acc.push({ ...cat, depth, children: undefined })
    if (cat.children && cat.children.length > 0) {
      acc.push(...flattenCategories(cat.children, depth + 1))
    }
    return acc
  }, [])
}

// Render as a nested menu (HTML example)
function renderMenu(categories) {
  if (!categories || categories.length === 0) return ''
  const items = categories.map(cat => {
    const sub = cat.children?.length > 0
      ? `<ul>${renderMenu(cat.children)}</ul>`
      : ''
    return `<li><a href="/blog/category/${cat.slug}">${cat.nombre}</a>${sub}</li>`
  })
  return items.join('')
}

Use cases

Blog navigation menu. Call this endpoint once at build time (or in a server component) and use the result to render a menu with dropdown support for subcategories. With the tree already structured, you don't need any additional grouping logic.

/category/[slug] pages. Generate a static route for each category using its slugs. In the route, use the category parameter from the posts endpoint to get the filtered posts: /api/v1/posts?web_id=...&category=technology. Combine both calls to have the category name and its posts on the same page.

"More in [category]" section. At the end of each post, show related posts from the same category. You have the category slug in the post's categories field — use it directly in the filter.

Dynamic breadcrumbs. With the parent_id field you can reconstruct the breadcrumb path from any category up to the root by traversing the tree upwards.

Tips and best practices

  • Cache the category tree aggressively — it changes very rarely compared to posts. A TTL of several hours, or manual invalidation when a category is modified, is sufficient.
  • Always use the slug to build category URLs, never the id. The slug is designed to appear in URLs and is readable by both users and search engines.
  • Check that children exists and has elements before iterating over it. Although the API always returns the field, it may be an empty array [].
  • If you use Astro with static generation, call this endpoint inside getStaticPaths to generate all category pages at build time with no runtime requests.

Common errors

Code Likely cause Fix
401 API Key not included or incorrect Check the X-API-Key header and that the key is active in the panel
400 Missing web_id parameter Include web_id in the query string
200 with data: [] The website has no categories yet Create at least one category from the panel before calling the endpoint

See also

  • Posts — filter posts by category using the category parameter
  • Tags — flat alternative for cross-cutting content organisation