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 | Sí | 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 |
|---|---|---|
id | UUID | Identificador único del post |
titulo | string | Título del post |
slug | string | URL amigable única por web e idioma |
excerpt | string | null | Resumen breve, si está definido |
contenido | string | HTML completo del artículo |
featured_image_url | string | null | URL absoluta de la imagen destacada |
status | string | Siempre published en la API pública |
published_at | ISO 8601 | Fecha y hora de publicación |
meta_title | string | null | Título SEO personalizado |
meta_description | string | null | Descripción SEO personalizada |
author.name | string | Nombre del autor |
categories | array | Categorías asignadas al post |
tags | array | Etiquetas 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) | Sí | Slug del post en la URL |
web_id | string | Sí | 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=10o 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
excerptpara las tarjetas de preview del blog. Nunca trunces el campocontenidomanualmente — el excerpt está pensado exactamente para eso y puede tener formato propio. - Maneja siempre el caso en que
featured_image_urlesnull. Prepara una imagen de placeholder o condiciona el renderizado con un check previo. - Usa el parámetro
langsiempre 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, usameta_titleymeta_descriptioncuando existan, y cae altituloyexcerptcomo 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