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.
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:ready ú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:readsolo 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… | Ruta | Permiso |
|---|---|---|
| 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.
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
};
});
}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-Afterantes de reintentar y, mientras tanto, sirve la copia en caché. VigilaX-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.
uniquesApproxes aproximado yuniquesApproxSumno es el número de personas distintas del periodo. Etiquétalo como «visitantes (aprox.)».
Más sobre las claves en Seguridad.