# API de RadarDeTrenes para desarrolladores y agentes

RadarDeTrenes publica una **API pública, gratuita y de solo lectura** con los datos ferroviarios que agrega de fuentes públicas de Renfe y ADIF: la posición GPS de más de 200 trenes en circulación (actualizada aproximadamente cada 20 segundos), los tableros oficiales de salidas y llegadas de cada estación, las líneas de Cercanías con su geometría, el catálogo técnico de material rodante y estadísticas de puntualidad. No hace falta clave de API, cuenta ni registro: basta con hacer la petición.

## Resumen

| Dato | Valor |
|---|---|
| URL base | `https://radardetrenes.com/api/v1` |
| Formato | JSON (UTF-8) con CORS abierto (`Access-Control-Allow-Origin: *`) |
| Precio | Gratuito, sin planes de pago ni claves; solo atribución ([detalle](#precio)) |
| Autenticación | Ninguna. Token Bearer opcional mediante `POST /agent/auth` |
| Límite de uso | 100 peticiones por segundo y por IP, anunciado en las cabeceras `RateLimit` |
| Especificación | [OpenAPI 3.1](/openapi.json) (también en `/api/openapi.json`) |
| Para agentes de IA | [llms.txt](/llms.txt), [llms-full.txt](/llms-full.txt), [servidor MCP](#mcp), [auth.md](/auth.md) |
| Esta página en Markdown | [/docs.md](/docs.md), o la misma URL con `Accept: text/markdown` |
| Estado del servicio | [/healthz](/healthz) y [/readyz](/readyz) |

## Inicio rápido

```bash
# Trenes de larga y media distancia en circulación ahora mismo
curl -s 'https://radardetrenes.com/api/v1/fleet?type=ld'

# Ficha de Madrid Puerta de Atocha (código de estación 60000)
curl -s https://radardetrenes.com/api/v1/stations/60000

# Tablero oficial de salidas y llegadas de esa estación
curl -s https://radardetrenes.com/api/v1/stations/60000/board-renfe

# ¿Sigue circulando el tren ld-05173? (siempre responde 200 con un discriminador)
curl -s https://radardetrenes.com/api/v1/trains/ld-05173/status
```

Respuesta recortada de `GET /api/v1/fleet`:

```json
{
  "trains": [
    {
      "id": "ld-05173",
      "trainCode": "05173",
      "type": "long_distance",
      "latitude": 40.4168,
      "longitude": -3.7038,
      "originName": "Madrid Puerta de Atocha",
      "destinationName": "Barcelona Sants",
      "nextStationName": "Zaragoza Delicias",
      "productName": "AVE",
      "delayMinutes": 5,
      "timestamp": "2026-08-22T10:15:00Z"
    }
  ],
  "count": 1,
  "updatedAt": "2026-08-22T10:15:03Z"
}
```

## Cuándo usar esta API

Es la herramienta adecuada cuando necesitas:

- Saber **qué trenes circulan ahora** en España y dónde están, con su retraso y su próxima parada (`/api/v1/fleet`).
- Consultar las **salidas y llegadas de una estación** concreta, con retrasos, andenes y composición cuando se publica (`/api/v1/stations/{code}/board-renfe`).
- **Seguir un tren** por su identificador: posición en vivo, itinerario detallado, ruta y si ya ha terminado el viaje (`/api/v1/trains/{id}`, `/stops`, `/route`, `/status`).
- Obtener el **trazado y las estaciones de una línea de Cercanías** o el catálogo de estaciones con coordenadas (`/api/v1/lines`, `/api/v1/stations`).
- Medir la **puntualidad** histórica de una estación o de un número de tren (`/api/v1/stats/...`).
- Consultar la **ficha técnica de una serie** de material rodante (`/api/v1/rolling-stock/{series}`).

No sirve para comprar billetes, consultar horarios oficiales completos ni confirmar cancelaciones: los datos proceden de feeds públicos que pueden retrasarse o interrumpirse, y que un tren no aparezca no demuestra por sí solo que esté cancelado. Para decisiones de viaje, consulta siempre los canales oficiales del operador.

## Endpoints

Todos responden a `GET` bajo `https://radardetrenes.com/api/v1`, salvo que se indique otra cosa. Los detalles de parámetros, esquemas y ejemplos están en la [especificación OpenAPI](/openapi.json).

| Ruta | Devuelve |
|---|---|
| `/fleet?type=ld,commuter,all` | Snapshot de la flota en circulación (`FleetResponse`) |
| `/fleet/stream?type=...` | Flujo SSE con el mismo snapshot cada vez que cambia |
| `/trains/{id}` | Registro en vivo de un tren |
| `/trains/{id}/route` | Paradas programadas y geometría |
| `/trains/{id}/stops` | Itinerario detallado con horas reales, retrasos y andenes |
| `/trains/{id}/status` | `live`, `finished` o `not_found`, siempre con 200 |
| `/stations?type=...` | Catálogo de estaciones con coordenadas, alias, líneas y slug |
| `/stations/{code}` | Una estación por código numérico |
| `/stations/{code}/alerts` | Avisos e incidencias operativas |
| `/stations/{code}/detail` | Ficha enriquecida de la estación |
| `/stations/{code}/board-renfe` | Tablero oficial de salidas y llegadas |
| `/lines` | Líneas de Cercanías con geometría GeoJSON |
| `/rolling-stock` y `/rolling-stock/{series}` | Catálogo y ficha de material rodante |
| `/stats/station/{code}?window=24h,7d,30d` | Estadísticas de puntualidad por estación |
| `/stats/train/{trainCode}?window=...` | Estadísticas de puntualidad por número de tren |
| `/routes` | Corredores ciudad a ciudad con página de fiabilidad publicada |
| `/healthz` y `/readyz` (en la raíz) | Liveness y readiness del servicio |

Los códigos de estación son los numéricos de Renfe/ADIF (60000 Madrid Puerta de Atocha, 71801 Barcelona Sants, 17000 Madrid Sol…) y se obtienen de `/stations`. Los ids de tren salen de `/fleet` (por ejemplo `ld-05173`).

## Autenticación y registro de agentes

La API pública no exige autenticación. Si tu agente necesita una credencial explícita (por ejemplo, porque su marco de trabajo la requiere), puede registrarse de forma autónoma y gratuita:

```bash
curl -s -X POST https://radardetrenes.com/agent/auth \
  -H 'Content-Type: application/json' \
  -d '{"type":"anonymous"}'
# → {"access_token":"...","token_type":"Bearer","expires_in":3600,"scope":"read:trains read:stations"}
```

El token se envía como `Authorization: Bearer <token>`; es opcional, de corta duración y solo personaliza el sujeto, porque todos los ámbitos son de lectura pública. El flujo completo (aserciones de identidad, OAuth 2.0 con PKCE, revocación y recuperación) está descrito en [auth.md](/auth.md) y en los metadatos [oauth-protected-resource](/.well-known/oauth-protected-resource) y [oauth-authorization-server](/.well-known/oauth-authorization-server).

## Tiempo real con Server-Sent Events

En lugar de sondear `/fleet` cada pocos segundos, suscríbete al flujo:

```bash
curl -N --http1.1 'https://radardetrenes.com/api/v1/fleet/stream?type=all'
```

```text
event: connected
data: {"filter":"all"}

event: fleet
data: {"trains":[...],"count":212,"updatedAt":"2026-08-22T10:15:03Z"}

: keepalive
```

El primer evento `fleet` llega de inmediato con el snapshot en caché; después se emite uno nuevo cada vez que cambia la flota (unos 20 segundos) y un comentario `keepalive` cada 15 segundos. El flujo se sirve sin compresión para evitar el almacenamiento intermedio antes del primer evento.

## Errores

Todas las respuestas de error son JSON con el formato de **RFC 9457 (Problem Details)**, tipo `application/problem+json`, y añaden dos miembros pensados para agentes: `code`, un identificador estable, y `hint`, una indicación de cómo resolverlo. El miembro `error` se conserva por compatibilidad con los clientes que leían `{"error": "..."}`.

```json
{
  "type": "https://radardetrenes.com/docs#error-not_found",
  "title": "Not Found",
  "status": 404,
  "detail": "station not found",
  "instance": "/api/v1/stations/99999999",
  "code": "not_found",
  "hint": "Verify the identifier; the endpoints and identifier formats are described in https://radardetrenes.com/openapi.json.",
  "error": "station not found"
}
```

### bad_request

`400`. Un parámetro o identificador no es válido (por ejemplo `type=bogus` o una ventana de estadísticas que no es `24h`, `7d` ni `30d`). Revisa el parámetro contra la especificación OpenAPI.

### unauthorized

`401`. La credencial presentada no es válida. La API pública no la necesita: repite la petición sin cabecera `Authorization` o registra un token nuevo.

### forbidden

`403`. La credencial no concede el ámbito solicitado o el origen no está permitido (en `/mcp`). Consulta [auth.md](/auth.md).

### not_found

`404`. No existe ningún recurso con ese identificador, o la ruta no forma parte del API. Comprueba el id o el código; la lista de rutas está en `/openapi.json`.

### method_not_allowed

`405`. La ruta existe pero no admite ese método. La cabecera `Allow` indica los métodos válidos (todas las rutas de datos son `GET`, `HEAD` y `OPTIONS`).

### not_acceptable

`406`. El recurso no puede servirse en el formato pedido. Las rutas del API devuelven `application/json`; las páginas, `text/html` o `text/markdown`.

### rate_limited

`429`. Se ha superado el límite de 100 peticiones por segundo por IP. Espera los segundos indicados en `Retry-After` y consulta la cabecera `RateLimit` para ajustar el ritmo.

### internal_error

`500`. Error inesperado del servidor. Reintenta más tarde y, si persiste, avísanos en [contacto](/contacto).

### upstream_unavailable

`502`. La fuente de datos ferroviaria de origen no respondió a tiempo (tableros, itinerarios o avisos). Suele resolverse en segundos: reintenta con una pequeña espera.

### service_unavailable

`503`. El servicio está temporalmente no disponible, por ejemplo mientras arranca (`/readyz` devuelve `warming_up`) o si el flujo SSE tiene demasiadas conexiones abiertas. Reintenta más tarde.

## Límites de uso y caché

- **Cuota:** 100 peticiones por segundo por dirección IP, con una ráfaga máxima de 100. Las respuestas del API y de los endpoints de agentes incluyen `RateLimit-Policy: "per-ip";q=100;w=1` y `RateLimit: "per-ip";r=<restantes>;t=<segundos>` según el borrador de la IETF *draft-ietf-httpapi-ratelimit-headers*.
- **Al superarla:** respuesta `429` con `Retry-After` (segundos) y cuerpo `application/problem+json` con `code: rate_limited`.
- **Caché:** las respuestas llevan `Cache-Control` con la frescura real de cada dato (5 segundos para la flota, 15 para los tableros, una hora para las líneas) y `ETag`; envía `If-None-Match` para recibir `304` cuando no haya cambios. Respeta esos valores: la flota no cambia más de una vez cada ~20 segundos.
- **Conexiones SSE:** hay un máximo de conexiones simultáneas por IP; comparte una sola suscripción en tu proceso.
- **Identifícate:** un `User-Agent` descriptivo con una URL o forma de contacto ayuda a diagnosticar problemas y evita bloqueos.

## Servidor MCP

RadarDeTrenes expone un servidor **Model Context Protocol** de solo lectura (transporte Streamable HTTP, versión de protocolo 2025-06-18) en `POST https://radardetrenes.com/mcp`, con tarjeta de servidor en [/.well-known/mcp/server-card.json](/.well-known/mcp/server-card.json). Herramientas disponibles:

- `buscar_tren` — localiza un tren en circulación por su número (`numero_tren`).
- `ver_tablero` — devuelve el enlace al tablero de salidas y llegadas de una estación por nombre o código (`estacion`).
- `ver_flota_actual` — resume el estado actual de la flota en circulación.

```bash
curl -s -X POST https://radardetrenes.com/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"buscar_tren","arguments":{"numero_tren":"03097"}}}'
```

Las peticiones `GET /mcp` responden `405` porque el servidor no ofrece flujo SSE; las notificaciones (sin `id`) responden `202`.

## Archivos de descubrimiento para agentes

| Recurso | Para qué sirve |
|---|---|
| [/llms.txt](/llms.txt) | Resumen del sitio, cuándo usarlo y enlaces clave (formato llmstxt.org) |
| [/llms-full.txt](/llms-full.txt) | Documentación completa en Markdown para contexto de agentes |
| [/openapi.json](/openapi.json) | Especificación OpenAPI 3.1 con `operationId`, esquemas y ejemplos |
| [/auth.md](/auth.md) | Instrucciones de registro y autenticación para agentes |
| [/.well-known/api-catalog](/.well-known/api-catalog) | Catálogo de APIs (RFC 9727, linkset) |
| [/.well-known/agent-skills/index.json](/.well-known/agent-skills/index.json) | Índice de habilidades con hash de integridad |
| [/.well-known/mcp/server-card.json](/.well-known/mcp/server-card.json) | Tarjeta del servidor MCP |
| [/.well-known/webmcp](/.well-known/webmcp) | Manifiesto de las herramientas WebMCP que registra la web |
| [/sitemap.xml](/sitemap.xml) y [/robots.txt](/robots.txt) | Índice de URLs y permisos de rastreo (los rastreadores de IA están permitidos) |

Las páginas principales negocian contenido: la portada y esta documentación devuelven Markdown si la petición lleva `Accept: text/markdown` (con `Vary: Accept`), y esta página también está en `/docs.md`. Además, cada respuesta HTML incluye cabeceras `Link` (RFC 8288) que apuntan a estos recursos.

## Datos, atribución y límites

- **Fuentes:** datos abiertos y servicios públicos de Renfe (posiciones, rutas, GTFS, alertas) y ADIF (tableros de salidas y llegadas); cartografía de OpenStreetMap y proveedores de teselas.
- **Naturaleza:** información orientativa, con posibles desfases, inexactitudes e interrupciones; RadarDeTrenes es un proyecto independiente y no oficial, sin relación con Renfe ni ADIF.
- **Reutilización:** los datos públicos se reutilizan con cita de fuente al amparo de la Ley 37/2007. Si reutilizas datos obtenidos a través de este API, mantén la atribución a Renfe y ADIF y cita a RadarDeTrenes (`https://radardetrenes.com`) como intermediario. El detalle de fuentes, licencias y aviso legal está en [Sobre RadarDeTrenes](/sobre).
- **Uso responsable:** el servicio es gratuito y sin publicidad; un uso abusivo perjudica al resto. Usa caché y el flujo SSE, y no redistribuyas el API como si fuera tuyo.

## Precio y condiciones de uso

La API, el servidor MCP y la web son **gratuitos**: no hay planes de pago, niveles premium, claves que solicitar ni formularios de contacto comercial. El único límite es técnico (100 peticiones por segundo por IP) y la única condición es la atribución descrita en la sección anterior. RadarDeTrenes es un proyecto independiente sin ánimo de lucro y sin publicidad; quien quiera apoyarlo puede hacerlo en [Ko-fi](https://ko-fi.com/pablitopool), pero nada de lo documentado aquí depende de ello.

## Versionado, desaprobación y soporte

La versión actual es `v1` y los campos existentes no cambian de significado; los campos nuevos se añaden sin aviso previo, así que ignora los que no conozcas. No hay ninguna retirada prevista. Si en el futuro hubiera que retirar o cambiar de forma incompatible un endpoint, se anunciará con antelación en esta página y el endpoint afectado empezará a responder con las cabeceras `Deprecation` y `Sunset` (RFC 9745 y RFC 8594) indicando la fecha, de modo que un agente pueda detectarlo sin leer prosa. La versión desplegada se puede consultar en `/healthz` y en las cabeceras `X-Radar-Version`, `X-Radar-Commit` y `X-Radar-Build-Time`. Para avisar de un error, proponer una mejora o cualquier otra consulta, escribe a contacto@radardetrenes.com o desde la página de [contacto](/contacto).
