Posts

Los posts son la unidad central de contenido editorial en Ágora CMS. Sirven para publicar artículos de blog, noticias, guías, novedades o cualquier contenido narrativo. Cada post tiene título, contenido HTML completo, excerpt, imagen destacada, metadatos SEO, categorías y etiquetas. La API expone dos endpoints: uno para obtener listados paginados con filtros, y otro para recuperar un post concreto por su slug.

GET /posts — Listado de posts

Devuelve los posts con estado published de una web, ordenados por fecha de publicación descendente. Admite filtrado por categoría, etiqueta y búsqueda de texto, además de paginación completa. Es el endpoint principal para construir listados de blog, páginas de archivo y resultados de búsqueda.

Parámetro Tipo Requerido Descripción
web_id string ID de la web
lang string No Código de idioma. Si se omite, devuelve el idioma por defecto
category string No Filtra por slug de categoría
tag string No Filtra por slug de etiqueta
search string No Búsqueda en título y contenido
page integer No Página. Por defecto: 1
limit integer No Resultados por página. Por defecto: 20, máximo: 100
status string No Por defecto solo devuelve published. No modificable desde la API pública.
curl "https://tu-panel.com/api/v1/posts?web_id=abc-123&lang=es&page=1&limit=10" \
  -H "X-API-Key: tu-api-key"
Respuesta esperada 200 OK
{
  "data": [
    {
      "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "titulo": "Mi primer artículo",
      "slug": "mi-primer-articulo",
      "excerpt": "Introducción breve al artículo...",
      "contenido": "<p>Contenido HTML completo...</p>",
      "featured_image_url": "https://tu-panel.com/uploads/abc/imagen.jpg",
      "status": "published",
      "published_at": "2026-01-15T10:00:00Z",
      "meta_title": "Mi primer artículo | Blog",
      "meta_description": "Descripción SEO del artículo...",
      "author": { "name": "Ana García" },
      "categories": [
        { "id": "...", "nombre": "Tecnología", "slug": "tecnologia" }
      ],
      "tags": [
        { "id": "...", "nombre": "API", "slug": "api" }
      ]
    }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "limit": 10,
    "pages": 5
  }
}

Campos de respuesta

Campo Tipo Descripción
idUUIDIdentificador único del post
titulostringTítulo del post
slugstringURL amigable única por web e idioma
excerptstring | nullResumen breve, si está definido
contenidostringHTML completo del artículo
featured_image_urlstring | nullURL absoluta de la imagen destacada
statusstringSiempre published en la API pública
published_atISO 8601Fecha y hora de publicación
meta_titlestring | nullTítulo SEO personalizado
meta_descriptionstring | nullDescripción SEO personalizada
author.namestringNombre del autor
categoriesarrayCategorías asignadas al post
tagsarrayEtiquetas asignadas al post

El campo contenido contiene el HTML completo del artículo tal como fue escrito en el editor. Para renderizarlo en tu frontend usa dangerouslySetInnerHTML en React, la directiva v-html en Vue, o la directiva set:html en Astro. Asegúrate de confiar en el origen del contenido — en este caso viene de tu propio CMS, así que es seguro.

GET /posts/{slug} — Post individual

Devuelve un post específico por su slug. Es el endpoint que usarás en las páginas de detalle de un artículo — tanto en generación estática (getStaticPaths en Next.js o getStaticPaths en Astro) como en SSR. El slug es único por web e idioma, lo que garantiza que no hay colisiones entre traducciones.

Parámetro Tipo Requerido Descripción
slug string (path) Slug del post en la URL
web_id string ID de la web
lang string No Código de idioma
curl "https://tu-panel.com/api/v1/posts/mi-primer-articulo?web_id=abc-123&lang=es" \
  -H "X-API-Key: tu-api-key"
Respuesta esperada 200 OK
{
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "titulo": "Mi primer artículo",
    "slug": "mi-primer-articulo",
    "excerpt": "Introducción breve al artículo...",
    "contenido": "<p>Contenido HTML completo...</p>",
    "featured_image_url": "https://tu-panel.com/uploads/abc/imagen.jpg",
    "status": "published",
    "published_at": "2026-01-15T10:00:00Z",
    "meta_title": "Mi primer artículo | Blog",
    "meta_description": "Descripción SEO del artículo...",
    "author": { "name": "Ana García" },
    "categories": [ { "id": "...", "nombre": "Tecnología", "slug": "tecnologia" } ],
    "tags": [ { "id": "...", "nombre": "API", "slug": "api" } ]
  }
}

Usa siempre el slug para identificar posts en las URLs de tu sitio — nunca el id UUID. El slug está pensado para ser legible y estable. El id es útil para referencias internas o cuando necesitas identificar un post de forma inequívoca independientemente del idioma o los cambios de slug. Si un post cambia de slug, el id permanece constante.

Casos de uso

Blog corporativo con Astro. En getStaticPaths llama al endpoint de listado para obtener todos los slugs y genera una ruta estática por post. En cada ruta, llama al endpoint individual para obtener el contenido completo. El resultado es un blog completamente estático, sin dependencias en runtime.

// src/pages/blog/[slug].astro
export async function getStaticPaths() {
  const res = await fetch(
    `${import.meta.env.API_URL}/posts?web_id=${import.meta.env.WEB_ID}&limit=100`,
    { headers: { 'X-API-Key': import.meta.env.API_KEY } }
  )
  const { data } = await res.json()
  return data.map(post => ({ params: { slug: post.slug } }))
}

Noticias con Next.js e ISR. Usa getStaticProps con revalidate: 3600 para regenerar las páginas cada hora sin necesidad de un build completo. Así tienes la velocidad de páginas estáticas con contenido casi en tiempo real.

Portfolio de proyectos. Si no necesitas las funcionalidades de categorías o campos personalizados, los posts son perfectos para un portfolio sencillo: cada proyecto es un post con su imagen destacada, excerpt y contenido HTML. Para portfolios más complejos con campos estructurados considera los tipos de contenido personalizados.

Página de categoría. Usa el parámetro category para filtrar posts por slug de categoría. Combínalo con la API de categorías para construir el menú de navegación y generar rutas estáticas por categoría.

Consejos y buenas prácticas

  • Usa limit=10 o menos en listados de blog. Páginas más ligeras cargan más rápido y mejoran el SEO.
  • Cachea las respuestas del servidor siempre que puedas. Los posts publicados no cambian con frecuencia — un TTL de 5-60 minutos es razonable según la frecuencia de publicación.
  • Usa el campo excerpt para las tarjetas de preview del blog. Nunca trunces el campo contenido manualmente — el excerpt está pensado exactamente para eso y puede tener formato propio.
  • Maneja siempre el caso en que featured_image_url es null. Prepara una imagen de placeholder o condiciona el renderizado con un check previo.
  • Usa el parámetro lang siempre que tu sitio sea multiidioma. Si lo omites obtienes el idioma por defecto configurado en el panel, lo que puede dar resultados inesperados en sitios con múltiples idiomas.
  • Para generar el <title> y <meta description> de cada post, usa meta_title y meta_description cuando existan, y cae al titulo y excerpt como fallback.

Errores frecuentes

Código Causa probable Solución
401 API Key no incluida, incorrecta o revocada Verifica que el header X-API-Key está presente y que la clave es válida en Ajustes → API
404 Post no encontrado o no publicado Comprueba que el slug es correcto, que el post existe y que su estado es published — los borradores no aparecen en la API pública
400 Falta el parámetro web_id Incluye siempre web_id en la query string. Lo encuentras en Ajustes → Webs
429 Demasiadas peticiones en poco tiempo Implementa caché en el servidor y usa retry con backoff. Ver sección Rate limiting en la introducción

Ver también

  • Categorías — filtra posts por categoría y construye menús de navegación
  • Etiquetas — filtra posts por etiqueta y construye nubes de tags
  • Media — accede a imágenes y archivos subidos al workspace
  • Tipos de contenido — para contenido estructurado más allá del blog