API para desarrolladores
Los mismos datos que el mapa, en JSON. Gratis, sin registro y sin clave. Úsala en tu web, tu app o tu investigación.
En un minuto
- Dirección base:
https://trenes.valfiguer.com/api/v1 - Solo lectura (GET), respuestas en JSON o GeoJSON, en UTF-8 y comprimidas si las pides con gzip.
- CORS abierto: puedes llamarla desde el navegador, desde cualquier dominio. No usa cookies ni clave.
- Horas como marca Unix en segundos (
*_ts) y como ISO 8601 en UTC (*_at). - Especificación OpenAPI: /api/openapi.json · Consola interactiva: /api/docs
curl -s --compressed https://trenes.valfiguer.com/api/v1/trains | jq '.features | length'Condiciones de uso
Los datos vienen de Renfe Data, FGC Dades Obertes y Open Data Euskadi (MovEuskadi), todos con licencia CC BY 4.0. Puedes usarlos para lo que quieras, también con fines comerciales, si citas la fuente. Una línea basta:
Datos: Renfe Data, FGC y MovEuskadi (CC BY 4.0) vía trenes.valfiguer.comNo es un sistema de seguridad. Las posiciones llegan con segundos o minutos de retraso y pueden faltar trenes. No la uses para decisiones de seguridad ni la presentes como información oficial; la oficial es la de cada operador y Adif.
Límites y caché
- Hasta 30 peticiones por segundo por IP, con ráfagas de 100. Por encima responde
429: espera un segundo y reintenta. - Las posiciones se renuevan cada 15–20 s en origen. Pedir
/trainsmás de una vez cada 5 s no trae nada nuevo. - Las respuestas grandes llevan
ETag: envíaIf-None-Matchy recibirás304sin cuerpo si no ha cambiado. - Si vas a mover mucho tráfico o necesitas el histórico completo, escríbenos a info@valfiguer.com.
Cómo leer un tren
delay_s: retraso en segundos según el operador;nullsi no lo publica (FGC solo dice si va en hora: miraflags).reported_at/age_s: cuándo se tomó la posición y su antigüedad al generar la respuesta.stale: truesi lleva más de 3 minutos sin actualizarse.flags: correcciones o avisos aplicados al dato, por ejemplo una hora corregida o una posición descartada por imposible.category:ld(alta velocidad y larga distancia),md(media distancia),cercaniasuotros(FGC y Metro Bilbao).
Rutas
Trenes en circulación
GET/api/v1/trains
Todos los trenes con posición publicada ahora, como GeoJSON FeatureCollection, con el estado de cada fuente en feeds.
Caché: 5 s, ETag · Probar
{
"type": "FeatureCollection",
"generated_at": "2026-11-01T08:12:30Z",
"generated_ts": 1793520750,
"attribution": "Fuentes: Renfe Data (data.renfe.com), FGC Dades Obertes … CC BY 4.0. …",
"feeds": [{ "id": "renfe_ld_positions", "ok": true, "stale": false, "data_age_s": 14 }],
"features": [{
"type": "Feature",
"id": "renfe_ld:<id>",
"geometry": { "type": "Point", "coordinates": [-3.6893, 40.4066] },
"properties": {
"source": "renfe_ld", "operator": "Renfe", "category": "ld",
"product": "AVE", "train_number": "00297", "nucleo": null,
"line_color": null, "origin": "Madrid-Puerta de Atocha", "destination": "Barcelona-Sants",
"status_text": "en", "stop_name": "Zaragoza-Delicias", "platform": "4",
"delay_s": 180, "reported_at": "2026-11-01T08:12:16Z", "age_s": 14,
"stale": false, "speed_kmh": 287, "bearing": 64, "flags": []
}
}]
}Un tren
GET/api/v1/trains/{id}
Un tren con su recorrido reciente (hasta 80 posiciones en track: lon, lat, hora) y su itinerario: paradas con hora programada y estimada (horario más el retraso actual).
| Parámetro | En | Qué es |
|---|---|---|
id | ruta | El id de /trains, por ejemplo renfe_cercanias:1004D27203C4a. |
Caché: 5 s · Probar
{
"id": "renfe_ld:00297",
"properties": { "…": "como en /trains" },
"track": [[-3.6893, 40.4066, 1793520736]],
"itinerary": {
"method": "horario_mas_retraso_actual",
"delay_s": 180,
"stops": [{
"code": "71801", "name": "Barcelona-Sants",
"arrival_ts": 1793530800, "estimated_arrival_ts": 1793530980,
"state": "upcoming"
}]
}
}Un tren por su número
GET/api/v1/trains/numero/{número}
Lo mismo que /trains/{id}, buscando por el número comercial que sale en el billete o en el panel. Responde 404 si ese número no tiene posición ahora.
| Parámetro | En | Qué es |
|---|---|---|
número | ruta | Número comercial, con o sin ceros a la izquierda. |
nucleo | consulta | Opcional. Distingue Cercanías de ciudades distintas: Madrid, Barcelona, Valencia… |
Caché: 5 s · Probar
{ "id": "renfe_cercanias:1004D27704C5", "properties": { "…": "…" }, "track": [], "itinerary": null }Horario de hoy de un tren
GET/api/v1/horario/numero/{número}
Las salidas de hoy de un número de tren según el calendario GTFS de Renfe, con sus paradas y si está cancelado. Sirve para trenes que aún no han salido.
| Parámetro | En | Qué es |
|---|---|---|
número | ruta | Número comercial. |
nucleo | consulta | Opcional, como arriba. |
Caché: 30 s · Probar
{
"method": "horario_gtfs",
"runs": [{
"trip_id": "0029712026-11-01", "day": "2026-11-01", "cancelled": false,
"product": "AVE", "origin": "Madrid-Puerta de Atocha", "destination": "Barcelona-Sants",
"departure_ts": 1793520000, "arrival_ts": 1793530800,
"itinerary": { "stops": [] }
}]
}Panel de una estación
GET/api/v1/stations/{código}
Trenes de las próximas dos horas en una estación: los que circulan, con hora estimada, y los que aún no han salido según el horario de hoy (state: scheduled). Incluye la ficha de la estación cuando Adif la publica.
| Parámetro | En | Qué es |
|---|---|---|
código | ruta | Código de estación de Renfe (hasta 5 cifras), el mismo de /stations. |
Caché: 10 s · Probar
{
"station": { "code": "18000", "name": "Madrid-Puerta de Atocha", "lat": 40.4066, "lon": -3.6893 },
"trains": [{
"id": null, "trip_id": "…", "state": "scheduled", "category": "cercanias",
"product": "C5", "train_number": "27704", "destination": "Humanes",
"planned_ts": 1793520660, "estimated_ts": null, "delay_s": null,
"cancelled": false, "terminates": false
}]
}Estaciones
GET/api/v1/stations
Todas las estaciones con su código y nombre, como GeoJSON de puntos.
Caché: 1 h, ETag · Probar
{ "type": "FeatureCollection", "features": [{ "geometry": { "type": "Point", "coordinates": [-3.6893, 40.4066] }, "properties": { "code": "18000", "name": "Madrid-Puerta de Atocha" } }] }Líneas
GET/api/v1/lines
Recorrido de las líneas de Cercanías, FGC y Metro Bilbao con su color oficial, como GeoJSON de líneas simplificadas a unos 15 m.
Caché: 1 h, ETag · Probar
{ "type": "FeatureCollection", "features": [{ "properties": { "nucleo": "Madrid", "line": "C5", "name": "Móstoles-El Soto - Humanes", "color": "#FFCB06", "text_color": "#000000" }, "geometry": { "type": "LineString", "coordinates": [] } }] }Puntualidad
GET/api/v1/puntualidad
Puntualidad diaria por línea y producto: viajes, viajes con retraso y suma de retrasos, a partir del último retraso publicado de cada tren.
| Parámetro | En | Qué es |
|---|---|---|
dias | consulta | Días hacia atrás, de 1 a 31 (7 por defecto). |
Caché: 5 min · Probar
{ "on_time_threshold_s": 300, "days": [{ "date": "2026-10-31", "partial": false, "groups": [{ "label": "C5", "nucleo": "Madrid", "trips": 412, "on_time": 371, "over_30": 2 }] }] }Avisos
GET/api/v1/alerts
Avisos activos de Cercanías publicados por Renfe, con las líneas a las que afectan.
Caché: 30 s · Probar
{ "alerts": [{ "id": "…", "text": "…", "start": "2026-11-01T05:00:00Z", "end": null, "lines": [{ "nucleo": "Madrid", "line": "C3" }] }] }Cancelaciones
GET/api/v1/cancellations
Trenes que Renfe da por cancelados ahora.
Caché: 30 s · Probar
{ "count": 1, "trains": [{ "trip_id": "…", "category": "md", "product": "MD", "train_number": "18073", "origin": "…", "destination": "…" }] }Estado
GET/api/v1/status
Resumen del servicio: número de trenes, puntualidad del momento y salud de cada fuente.
Caché: 10 s · Probar
{ "trains": { "total": 812, "fresh": 790, "by_category": { "ld": 120, "cercanias": 610 } }, "delay": { "known": 700, "on_time_share": 0.91 }, "cancelled": 11, "feeds": [{ "id": "renfe_ld_positions", "ok": true, "data_age_s": 14 }] }Ejemplos
JavaScript, en el navegador o en Node 18+:
const res = await fetch("https://trenes.valfiguer.com/api/v1/trains");
const { features } = await res.json();
const late = features.filter((f) => (f.properties.delay_s ?? 0) > 600);
console.log(`${late.length} trenes con más de 10 min de retraso`);Python:
import httpx
board = httpx.get("https://trenes.valfiguer.com/api/v1/stations/18000").json()
for t in board["trains"][:5]:
print(t["product"], t["destination"], t["delay_s"])Mapa con MapLibre o Leaflet: añade la respuesta de /trains como fuente GeoJSON y refréscala cada 15 s.
map.addSource("trenes", { type: "geojson", data: "https://trenes.valfiguer.com/api/v1/trains" });
setInterval(() => map.getSource("trenes").setData("https://trenes.valfiguer.com/api/v1/trains"), 15000);Versiones
La v1 es estable: podemos añadir campos, pero no quitarlos ni cambiar su significado sin publicar una v2 y mantener la v1 al menos seis meses. Tu código debe ignorar los campos que no conozca.
Más sobre las fuentes, su frecuencia y el registro histórico en Datos.
Widgets para tu web
Un panel de salidas de una estación o los trenes de una línea, dentro de tu página. Se actualizan solos y ajustan su altura al contenido. Pega el bloque donde quieras que aparezca:
<div data-trenes-estacion="18000"></div>
<script src="https://trenes.valfiguer.com/embed.js" async></script>Una línea: núcleo y línea, tal como aparecen en el mapa, en minúsculas y con guiones.
<div data-trenes-linea="madrid/c5"></div>
<div data-trenes-linea="rodalies-de-catalunya/r2-nord"></div>
<div data-trenes-linea="fgc/s1"></div>
<script src="https://trenes.valfiguer.com/embed.js" async></script>data-tema="claro"u"oscuro": fija el tema. Sin él, sigue el del visitante.data-filas="6": cuántos trenes mostrar (estación: 1–15, 6 por defecto; línea: 1–20, 8 por defecto).- El código de estación es el de Renfe, el mismo de /stations/{código} y de la dirección de cada página de estación.
- Si añades widgets después de cargar la página (una aplicación de una sola página), llama a
window.TrenesEmbed.scan().
Sin JavaScript, con un iframe de altura fija:
<iframe src="https://trenes.valfiguer.com/embed/estacion/18000?tema=claro&filas=5"
title="Próximos trenes" loading="lazy"
style="width:100%;height:360px;border:0"></iframe>El pie del widget lleva la atribución que piden las licencias (Renfe Data, FGC y MovEuskadi, CC BY 4.0) y un enlace a la página completa: no lo tapes ni lo quites. Solo las rutas /embed/ se pueden mostrar dentro de otras webs.
Panel a pantalla completa
Para una televisión o una tableta en un vestíbulo, una oficina o una cafetería: /panel/{código} muestra las próximas salidas en grande, con reloj, se actualiza cada 15 s, ajusta el tamaño de la letra a la pantalla y, donde el navegador lo permite, impide que la pantalla se apague.
https://trenes.valfiguer.com/panel/18000
https://trenes.valfiguer.com/panel/18000?tema=claro&filas=8Oscuro por defecto; ?tema=claro para salas con mucha luz. ?filas= admite de 3 a 20. Cada página de estación tiene un enlace «Modo panel».
Cliente JavaScript
Un módulo pequeño, sin dependencias, para el navegador o Node 18+. Tipos en /sdk/trenes.d.ts.
import { createClient } from "https://trenes.valfiguer.com/sdk/trenes.js";
const api = createClient(); // o createClient({ baseUrl, timeoutMs, fetch })
const { features } = await api.trains();
const board = await api.station("18000"); // null si el código no existe
const runs = await api.schedule("00297");
const tren = await api.train(features[0].id);
const lineas = await api.lines();También exporta cada función suelta (trains, train, station, schedule, lines…). Un 404 devuelve null; cualquier otro error lanza TrenesError con su status. Para TypeScript, descarga el .d.ts junto al módulo o copia los tipos que necesites.
Python 3.10+, solo con la biblioteca estándar:
import json
import urllib.request
BASE = "https://trenes.valfiguer.com/api/v1"
def get(path: str):
req = urllib.request.Request(BASE + path, headers={"Accept": "application/json"})
with urllib.request.urlopen(req, timeout=10) as res:
return json.load(res)
board = get("/stations/18000")
print(board["station"]["name"])
for t in board["trains"][:8]:
retraso = "sin dato" if t["delay_s"] is None else f"{round(t['delay_s'] / 60)} min"
print(t["product"], t["train_number"], "→", t["destination"], retraso)
# Datos: Renfe Data, FGC y MovEuskadi (CC BY 4.0) vía trenes.valfiguer.comFeeds RSS
Avisos y cancelaciones en formato RSS, para tu lector de noticias o para automatizar alertas. Se pueden filtrar por núcleo y por línea con ?nucleo=madrid&linea=c5; cada página de línea enlaza los suyos.
- /api/v1/feeds/avisos.xml: avisos activos de Cercanías y Rodalies.
- /api/v1/feeds/cancelaciones.xml: trenes que Renfe marca como cancelados ahora.
Reflejan lo que Renfe publica en sus datos abiertos, con sus mismos huecos. Licencia CC BY 4.0.