# Artículos

> Endpoints GET /blogs/{blogId}/articles y GET /articles/{id} de la API de Blogenia: lista paginada y artículo completo en JSON, HTML o Markdown.

Versión web: https://blogelia.com/docs/articulos

Solo se devuelven artículos **publicados**. Los borradores, los programados y los pendientes de aprobación no aparecen.

## Listar artículos

**`GET /blogs/{blogId}/articles`**: Lista los artículos publicados de un blog, del más reciente al más antiguo.

**Parámetros de ruta**

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `blogId` | string | Sí | Identificador del blog. |

**Parámetros de consulta**

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `page` | integer | No | Página, desde 1 (por defecto 1, máximo 500). Ver [Paginación](https://blogelia.com/docs/paginacion). |
| `limit` | integer | No | Artículos por página, de 1 a 50 (por defecto 20). |
| `category` | string | No | Slug de una categoría del blog. Si no existe, la lista llega vacía. |
| `publishedAfter` | string | No | Fecha ISO 8601: solo artículos publicados después. |
| `status` | string | No | Si lo envías, solo admite `published`. |

**Campos de la respuesta**

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `data` | array | Artículos, con los campos de la tabla siguiente. |
| `page` | integer | Página devuelta. |
| `limit` | integer | Tamaño de página aplicado. |
| `total` | integer | Total de artículos que cumplen el filtro. |

**Campos de cada artículo de la lista**

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `id` | string | Identificador del artículo (24 caracteres hexadecimales). |
| `title` | string | Título. |
| `slug` | string | Último segmento de la URL pública. |
| `url` | string | URL pública del artículo en el blog. |
| `excerpt` | string | Extracto. Cadena vacía si no tiene. |
| `publishedAt` | string | Fecha de publicación, ISO 8601 en UTC. |
| `updatedAt` | string | Última modificación del contenido, ISO 8601 en UTC. Si no consta, igual que `publishedAt`. |
| `category` | object | Categoría del artículo: `{ slug, name }`. Si no tiene una propia, la categoría principal del blog. |
| `coverImage` | object \| null | Portada: `{ url, alt }`. `null` si el artículo no tiene. |
| `readingMinutes` | number | Minutos de lectura estimados (mínimo 1). |
| `faqs` | array | Preguntas frecuentes: `[{ q, a }]`. Lista vacía si no tiene. |

> **Nota:** La lista no incluye el contenido. Para obtenerlo, pide el artículo por su `id`.

**Petición**

*curl*

```bash
curl "https://api.blogelia.com/v1/blogs/66f1c0a2b3c4d5e6f7a8b9c0/articles?limit=5&category=reformas" \
  -H "Authorization: Bearer $BLOGENIA_API_KEY"
```

*Node.js*

```js
const res = await fetch('https://api.blogelia.com/v1/blogs/66f1c0a2b3c4d5e6f7a8b9c0/articles?limit=5&category=reformas', {
  headers: { Authorization: 'Bearer ' + process.env.BLOGENIA_API_KEY },
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const body = await res.json();
console.log(body);
```

*Python*

```python
import os
import requests

res = requests.get(
    "https://api.blogelia.com/v1/blogs/66f1c0a2b3c4d5e6f7a8b9c0/articles",
    params={"limit": 5, "category": "reformas"},
    headers={"Authorization": "Bearer " + os.environ["BLOGENIA_API_KEY"]},
    timeout=10,
)
res.raise_for_status()
print(res.json())
```

**Respuesta**

```json
{
  "data": [
    {
      "id": "66f1c0a2b3c4d5e6f7a8b9c1",
      "title": "¿Cuánto cuesta reformar una cocina?",
      "slug": "cuanto-cuesta-reformar-una-cocina",
      "url": "https://reformas-ejemplo.blogelia.com/cuanto-cuesta-reformar-una-cocina",
      "excerpt": "Precios orientativos y qué incluye cada partida.",
      "publishedAt": "2026-10-01T09:00:00.000Z",
      "updatedAt": "2026-10-02T10:30:00.000Z",
      "category": { "slug": "reformas", "name": "Reformas" },
      "coverImage": { "url": "https://cdn.example.com/cover.webp", "alt": "Cocina reformada" },
      "readingMinutes": 5,
      "faqs": [{ "q": "¿Cuánto tarda una reforma?", "a": "Entre dos y cuatro semanas." }]
    }
  ],
  "page": 1,
  "limit": 5,
  "total": 12
}
```

**Respuestas**

| Código | Cuándo |
| --- | --- |
| `200` | Lista devuelta (puede estar vacía). |
| `400` | Parámetro no válido, por ejemplo `limit` fuera de 1–50, `page` mayor que 500 o `publishedAfter` que no es una fecha. |
| `401` | Clave ausente, no válida o revocada. |
| `403` | Plan Free u organización suspendida. |
| `404` | El blog no existe, es de otra organización o está suspendido. |
| `429` | Límite del plan superado. |

## Obtener un artículo

**`GET /articles/{id}`**: Devuelve un artículo publicado con su contenido.

**Parámetros de ruta**

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `id` | string | Sí | Identificador del artículo (24 caracteres hexadecimales). |

**Parámetros de consulta**

| Nombre | Tipo | Obligatorio | Descripción |
| --- | --- | --- | --- |
| `format` | string | No | `json` (por defecto), `html` o `markdown`. Con `html` o `markdown` la respuesta es solo el contenido, con `Content-Type` `text/html` o `text/markdown`. |

Con `format=json` la respuesta tiene todos los campos de un artículo de la lista y, además, estos:

**Campos adicionales (format=json)**

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `contentHtml` | string | Fragmento HTML saneado, listo para incrustar en tu página. |
| `contentMarkdown` | string | El mismo contenido en Markdown. |
| `seo` | object | `{ title, description, canonical }`. Si el artículo no define los suyos, se rellenan con el título, el extracto y la `url`. |
| `sponsored` | boolean | `true` en artículos patrocinados: sus enlaces externos llevan `rel="sponsored"`. |

**Petición**

*curl*

```bash
curl "https://api.blogelia.com/v1/articles/66f1c0a2b3c4d5e6f7a8b9c1" \
  -H "Authorization: Bearer $BLOGENIA_API_KEY"
```

*Node.js*

```js
const res = await fetch('https://api.blogelia.com/v1/articles/66f1c0a2b3c4d5e6f7a8b9c1', {
  headers: { Authorization: 'Bearer ' + process.env.BLOGENIA_API_KEY },
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const body = await res.json();
console.log(body);
```

*Python*

```python
import os
import requests

res = requests.get(
    "https://api.blogelia.com/v1/articles/66f1c0a2b3c4d5e6f7a8b9c1",
    headers={"Authorization": "Bearer " + os.environ["BLOGENIA_API_KEY"]},
    timeout=10,
)
res.raise_for_status()
print(res.json())
```

**Respuesta**

```json
{
  "id": "66f1c0a2b3c4d5e6f7a8b9c1",
  "title": "¿Cuánto cuesta reformar una cocina?",
  "slug": "cuanto-cuesta-reformar-una-cocina",
  "url": "https://reformas-ejemplo.blogelia.com/cuanto-cuesta-reformar-una-cocina",
  "excerpt": "Precios orientativos y qué incluye cada partida.",
  "publishedAt": "2026-10-01T09:00:00.000Z",
  "updatedAt": "2026-10-02T10:30:00.000Z",
  "category": { "slug": "reformas", "name": "Reformas" },
  "coverImage": null,
  "readingMinutes": 5,
  "faqs": [],
  "contentHtml": "<h2>Precio medio</h2><p>…</p>",
  "contentMarkdown": "## Precio medio\n\n…",
  "seo": {
    "title": "¿Cuánto cuesta reformar una cocina?",
    "description": "Precios orientativos y qué incluye cada partida.",
    "canonical": "https://reformas-ejemplo.blogelia.com/cuanto-cuesta-reformar-una-cocina"
  },
  "sponsored": false
}
```

**Solo el HTML o el Markdown**

```bash
curl "https://api.blogelia.com/v1/articles/66f1c0a2b3c4d5e6f7a8b9c1?format=markdown" \
  -H "Authorization: Bearer $BLOGENIA_API_KEY"
```

**Respuestas**

| Código | Cuándo |
| --- | --- |
| `200` | Artículo devuelto. |
| `400` | `format` no es `json`, `html` ni `markdown`. |
| `401` | Clave ausente, no válida o revocada. |
| `403` | Plan Free u organización suspendida. |
| `404` | No existe, no está publicado, es de otra organización o su blog está suspendido. |
| `429` | Límite del plan superado. |
