Menú de la documentación

Desarrolladores

Construir tu dashboard

Guía para llevar las visitas y los artículos de Blogenia a tu propio dashboard: qué rutas llamar, cuánto cachear y un ejemplo en Node.js.

Ver como Markdown

Con la API puedes mostrar en tu propio panel las visitas de tus blogs, los artículos más leídos y el estado de la cuenta. Esta guía resume qué rutas llamar y cómo hacerlo de forma segura y sin gastar peticiones de más.

1. Crea una clave con el permiso justo

  • Para un dashboard de analítica, crea una clave solo con `stats:read`. No da acceso a artículos ni a datos de la cuenta, así que, si se filtra, el daño es mínimo.
  • Si además necesitas créditos, plan o estado de artículos, crea otra clave aparte con account:read y úsala solo en el servidor. No mezcles los dos permisos en una clave.
  • Los permisos se eligen al crear la clave, en **API** del panel, y no se pueden ampliar después. Si te falta uno, crea otra clave y revoca la antigua. account:read solo puede concederlo el propietario de la cuenta.

2. Llama siempre desde tu servidor

Las rutas de analítica y de cuenta no admiten CORS, a propósito: el navegador no puede llamarlas. Tu servidor pide los datos y tu front-end los pide a tu servidor. Nunca pongas la clave en código del navegador.

3. Qué rutas llamar

Quieres mostrar…RutaPermiso
Visitas totales de la cuenta y por blog`GET /account/stats`stats:read
Visitas de un blog por día, semana o mes`GET /blogs/{blogId}/stats`stats:read
Artículos más leídos`GET /blogs/{blogId}/stats/articles`stats:read
Visitas de un artículo`GET /articles/{id}/stats`stats:read
Créditos, plan, uso del mes y blogs`GET /account`account:read
Cuántos artículos hay por estado`GET /blogs/{blogId}/articles/summary`account:read
Qué está en marcha o ha fallado`GET /blogs/{blogId}/generations`account:read
Próximos huecos del piloto`GET /blogs/{blogId}/autopilot`account:read

4. Cachea 10 minutos

Las estadísticas se actualizan cada 10 minutos, así que pedirlas más a menudo no te da datos nuevos y gasta tu límite de uso. Guarda la respuesta en tu servidor 10 minutos (en memoria, Redis o donde prefieras) y sirve esa copia a todos tus visitantes.

5. Ejemplo en Node.js

Sin SDK, solo fetch (Node 18 o posterior). Pide el total de la cuenta y los cinco artículos más leídos de un blog, y lo guarda 10 minutos. Si Blogenia responde 429 o falla, sirve el dato anterior.

Módulo blogenia.js
const API = 'https://api.blogelia.com/v1';
const TTL_MS = 10 * 60 * 1000; // los datos se actualizan cada 10 minutos
const cache = new Map();

async function blogenia(path, query = {}) {
  const url = new URL(API + path);
  for (const [k, v] of Object.entries(query)) url.searchParams.set(k, String(v));
  const res = await fetch(url, {
    headers: { Authorization: 'Bearer ' + process.env.BLOGENIA_STATS_KEY },
  });
  const remaining = res.headers.get('X-RateLimit-Remaining');
  if (remaining !== null && Number(remaining) < 5) console.warn('Quedan pocas peticiones:', remaining);
  if (res.status === 429) {
    const err = new Error('rate_limited');
    err.retryAfter = Number(res.headers.get('Retry-After') ?? 60);
    throw err;
  }
  if (!res.ok) throw new Error('HTTP ' + res.status);
  return res.json();
}

async function cached(key, load) {
  const hit = cache.get(key);
  if (hit && Date.now() - hit.at < TTL_MS) return hit.value;
  try {
    const value = await load();
    cache.set(key, { at: Date.now(), value });
    return value;
  } catch (err) {
    if (hit) return hit.value; // ante un 429 o un fallo, sirve el dato anterior
    throw err;
  }
}

export function dashboardData(blogId) {
  return cached('dashboard:' + blogId, async () => {
    const [account, top] = await Promise.all([
      blogenia('/account/stats', { interval: 'day' }),
      blogenia('/blogs/' + blogId + '/stats/articles', { limit: 5 }),
    ]);
    return {
      views: account.totals.views,
      uniquesApproxSum: account.totals.uniquesApproxSum,
      series: account.series, // el último punto puede llevar partial: true
      blogs: account.blogs,
      topArticles: top.data,
      updatedAt: account.updatedAt, // puede ser null
    };
  });
}
Ruta de tu servidor
import express from 'express';
import { dashboardData } from './blogenia.js';

const app = express();

// Tu navegador llama a TU servidor; solo tu servidor conoce la clave.
app.get('/api/dashboard/:blogId', async (req, res) => {
  try {
    res.json(await dashboardData(req.params.blogId));
  } catch (err) {
    res.status(err.retryAfter ? 503 : 502).json({ error: 'No hay datos por ahora' });
  }
});

6. Casos que debes tratar

  • `partial: true`. El último grupo de la serie incluye hoy y aún no ha terminado. Píntalo distinto (por ejemplo, con una línea discontinua) o déjalo fuera de las comparaciones con periodos completos.
  • `updatedAt: null`. Todavía no se conoce la hora del último volcado de estadísticas. No muestres «actualizado a las…»; las cifras siguen siendo válidas.
  • `429`. Superaste el límite del plan. Espera los segundos de Retry-After antes de reintentar y, mientras tanto, sirve la copia en caché. Vigila X-RateLimit-Remaining. Ver Límites de uso.
  • `403` `insufficient_scope`. La clave no tiene el permiso de esa ruta. Crea otra con el permiso adecuado.
  • Únicos. uniquesApprox es aproximado y uniquesApproxSum no es el número de personas distintas del periodo. Etiquétalo como «visitantes (aprox.)».

Más sobre las claves en Seguridad.