GET /api/v1/site
Datos del sitio, plantilla y settings públicos (hero, newsletter, comentarios).
Parámetros: Ninguno (solo auth). Cache + ETag ~45s.
Respuesta: data { name, slug, domain, template, settings }
Documentación completa
Referencia completa de Blog Listo: auth, kit embed, endpoints, JSON de respuesta, campos, errores y ejemplos por lenguaje. Todo en esta página, con el mismo diseño de la web informativa.
Inicio rápido
Regístrate gratis como empresa y copia tu API key en el panel.
Embed: pegas CSS + JS. API: pides JSON y lo renders con tu diseño.
Crea categorías, tags y posts. Solo los publicados (y no programados a futuro) aparecen en la API.
Llama a GET /posts con tu key. Si ves data[], la conexión está bien.
Autenticación
Autenticación de la API pública con site key. Lectura de contenido + POST de newsletter y comentarios.
X-Site-Key: TU_SITE_KEY?site_key=TU_SITE_KEYhttps://platform-mi-blog-listo.tinguar.com/api/v1La key la obtienes al crear tu cuenta o empresa en el panel. En los ejemplos usa TU_SITE_KEY hasta copiar la tuya.
Por lenguaje
Elige tu stack. El código cambia al instante: HTML, JS, PHP, Python, WordPress, Astro, Laravel o React/Next.
<link rel="stylesheet" href="https://platform-mi-blog-listo.tinguar.com/embed/blog-templates.css" />
<div id="blog-root"></div>
<script src="https://platform-mi-blog-listo.tinguar.com/embed/blog-client.js"></script>
<script>
BlogPlatform.create({
apiBase: 'https://platform-mi-blog-listo.tinguar.com/api/v1',
siteKey: 'TU_SITE_KEY',
listEl: '#blog-root',
}).bootFromQuery();
</script>Kit embed
Archivos públicos del panel: https://platform-mi-blog-listo.tinguar.com/embed/blog-templates.css y https://platform-mi-blog-listo.tinguar.com/embed/blog-client.js. Ideal si quieres listado + detalle sin armar el front desde cero.
| Opción | ¿Obligatoria? | Descripción |
|---|---|---|
apiBase | sí | Base de la API, ej. https://platform-mi-blog-listo.tinguar.com/api/v1 |
siteKey | sí | Tu API key del panel |
listEl | sí | Selector o nodo del listado, ej. #blog-root |
detailEl | no | Selector o nodo del detalle; si no hay, usa listEl |
El kit incluye spinner, búsqueda, filtros, paginación, lightbox, compartir, tema oscuro, progreso de lectura, newsletter y comentarios con respuestas anidadas según settings. Las vistas del detalle (y UTM de la URL) se registran en el panel.
| Método | Qué hace |
|---|---|
bootFromQuery() | Si la URL tiene ?slug=mi-post muestra detalle; si no, el listado (también lee featured, category, tag, q, page). |
renderList() | Listado con búsqueda, filtros (categoría/tag/destacados), paginación, newsletter y tema claro/oscuro. |
renderPost(slug) | Detalle: lightbox, compartir, progreso de lectura, comentarios, related. Registra vista + UTM. |
api(path) | Fetch interno a la API con tu site key; en detalle reenvía utm_* de location.search. |
Detalle con query: /blog?slug=mi-primer-post → bootFromQuery() carga el artículo. Demo: https://platform-mi-blog-listo.tinguar.com/embed/example.html
API
Auth por site key. GET con caché/ETag salvo el detalle del post; POST con rate limit más bajo.
GET /api/v1/site
Datos del sitio, plantilla y settings públicos (hero, newsletter, comentarios).
Parámetros: Ninguno (solo auth). Cache + ETag ~45s.
Respuesta: data { name, slug, domain, template, settings }
GET /api/v1/posts
Listado paginado de posts publicados (no incluye programados a futuro).
Parámetros: page, per_page (1–50, default 12), category (slug), tag (slug), featured (1|true), q (búsqueda en título/subtítulo/extracto)
Respuesta: data[], meta { paginación + stats }, template. Cache + ETag.
GET /api/v1/posts/{slug}
Detalle publicado. Registra vista (únicos + UTM/referrer) y suma view_count.
Parámetros: slug; opcionales utm_source, utm_medium, utm_campaign, utm_content, utm_term (el embed los reenvía desde la URL de la página).
Respuesta: data (+ content, related[], comments[], tags[]), settings, template. Sin caché (para medir vistas).
GET /api/v1/categories
Categorías del sitio con conteo de posts.
Parámetros: Ninguno (solo auth). Cache + ETag.
Respuesta: data[] { name, slug, posts_count }
GET /api/v1/tags
Tags del sitio con conteo de posts.
Parámetros: Ninguno (solo auth). Cache + ETag.
Respuesta: data[] { name, slug, posts_count }
GET /api/v1/feed.xml
Feed RSS de posts publicados.
Parámetros: base_url (URL pública del blog del cliente, para links absolutos).
Respuesta: application/rss+xml
GET /api/v1/sitemap.xml
Sitemap XML de posts publicados.
Parámetros: base_url (URL pública del blog).
Respuesta: application/xml
GET /api/v1/posts/{slug}/comments
Árbol de comentarios visibles del post (si comments_enabled). Incluye respuestas anidadas.
Parámetros: slug. Cache + ETag.
Respuesta: data[] { id, parent_id, depth, author_name, body, is_featured, staff_label, created_at, created_label, replies[] }
POST /api/v1/posts/{slug}/comments
Publicar comentario o respuesta. Rate limit de escritura más estricto.
Parámetros: JSON: author_name, body; opcionales author_email, parent_id (respuesta, máx. 4 niveles), website (honeypot).
Respuesta: 201 + data { …, replies: [] } o pendiente si hay moderación
POST /api/v1/newsletter
Suscripción al newsletter del sitio (si newsletter_enabled).
Parámetros: JSON: email; opcionales source, website (honeypot).
Respuesta: 200 + mensaje de confirmación
Contrato JSON
Forma típica de GET /posts y GET /posts/{slug}.
{
"data": [
{
"title": "Mi primer post",
"subtitle": null,
"slug": "mi-primer-post",
"excerpt": "Resumen…",
"cover_image": "https://…/cover.jpg",
"cover_layout": "below",
"cover_size": "full",
"is_featured": false,
"view_count": 42,
"reading_minutes": 3,
"published_at": "2026-08-10T12:00:00+00:00",
"published_label": "10 ago 2026",
"author": { "name": "Ana" },
"category": { "name": "Noticias", "slug": "noticias" },
"tags": [{ "name": "Guías", "slug": "guias" }]
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 12,
"total": 1,
"stats": { "articles": 1, "categories": 1, "featured": 0 }
},
"template": { "key": "classic", "name": "Classic", "config": {} }
}{
"data": {
"title": "Mi primer post",
"slug": "mi-primer-post",
"excerpt": "Resumen…",
"content": "<p>HTML del artículo…</p>",
"meta_title": "Mi primer post",
"meta_description": "Resumen…",
"cover_image": "https://…/cover.jpg",
"view_count": 43,
"author": { "name": "Ana" },
"category": { "name": "Noticias", "slug": "noticias" },
"tags": [{ "name": "Guías", "slug": "guias" }],
"related": [{ "title": "Otro post", "slug": "otro-post" }],
"comments": [{
"id": 12,
"parent_id": null,
"depth": 0,
"author_name": "Luis",
"body": "Muy útil",
"is_featured": false,
"staff_label": null,
"created_label": "hace 2 horas",
"replies": [{
"id": 15,
"parent_id": 12,
"depth": 1,
"author_name": "Ana",
"body": "¡Gracias!",
"is_featured": false,
"staff_label": "Autor",
"created_label": "hace 1 hora",
"replies": []
}]
}]
},
"settings": {
"newsletter_enabled": true,
"comments_enabled": true,
"blog_show_hero": true
},
"template": { "key": "classic", "name": "Classic", "config": {} }
}Datos
Todos los campos que expone la API hoy.
| Campo | Descripción | Dónde |
|---|---|---|
title | Título | listado + detalle |
subtitle | Subtítulo opcional | listado + detalle |
slug | Para /blog/{slug} o ?slug= | listado + detalle |
excerpt | Resumen corto | listado + detalle |
content | HTML del cuerpo | solo detalle |
cover_image | URL de portada (o null) | listado + detalle |
cover_layout | Disposición de portada (ej. below) | listado + detalle |
cover_size | Tamaño de portada (ej. full) | listado + detalle |
is_featured | true si está destacado | listado + detalle |
view_count | Visualizaciones del artículo (se incrementa al abrir el detalle) | listado + detalle |
reading_minutes | Minutos estimados de lectura | listado + detalle |
published_at | Fecha ISO 8601 | listado + detalle |
published_label | Fecha legible para UI | listado + detalle |
author.name | Nombre del autor | listado + detalle |
category.name | Nombre de categoría | listado + detalle |
category.slug | Slug de categoría (filtro) | listado + detalle |
tags[] | Tags { name, slug } | listado + detalle |
related[] | Hasta 3 posts relacionados (misma categoría) | solo detalle |
comments[] | Árbol visible (replies[], is_featured, staff_label Empresa/Autor/Escritor) | solo detalle |
meta_title | Título SEO | solo detalle |
meta_description | Descripción SEO | solo detalle |
Panel
Lo que configuras en el panel afecta la API y el embed.
Errores
HTTP 401 — Falta la key
{ "message": "Missing site_key. Send X-Site-Key header or site_key query param." }HTTP 401 — Key inválida o sitio inactivo
{ "message": "Invalid or inactive site key." }HTTP 404 — Post no existe, no está publicado o aún está programado
Respuesta estándar Laravel Not FoundHTTP 429 — Rate limit (lectura ~120/min; escritura newsletter/comentarios ~20/min por sitio+IP)
Too Many Attempts.HTTP 304 — ETag válido en listados cacheados (If-None-Match)
Cuerpo vacío; reutiliza la respuesta anterior del cliente.En tu sitio
tudominio.com/blogtudominio.com/blog?slug=mi-posttudominio.com/blog?slug=mi-post&utm_source=newsletter?category=noticias&tag=guias&q=netflix&page=2tudominio.com/blog/mi-post (tú haces el routing y pides /posts/mi-post)Notas
Crea tu cuenta gratis, copia la API key y sigue el ejemplo de tu lenguaje. Si no puedes solo, hay ayuda a precio accesible.