Quiniela NFL entre amigos
De la hoja de cálculo olvidada a una plataforma con FastAPI, React y Docker que consulta ESPN, calcula las posiciones y se actualiza sola cada 90 segundos.
Cada septiembre se repite el ritual: arranca la NFL y con ella la quiniela entre amigos. Durante años el operativo fue artesanal — alguien actualizaba una hoja de cálculo a mano, los marcadores circulaban por WhatsApp y las posiciones llegaban tarde, o no llegaban. Este año decidí resolverlo como resolvería cualquier problema de producto: construyendo una herramienta que hiciera el trabajo sucio sola.
El resultado: Quiniela NFL, una plataforma web con diseño Material Design, 100% responsiva (celular, tablet, escritorio), corriendo en un contenedor Docker y accesible desde cualquier dispositivo.
Las reglas del juego
La mecánica es la clásica quiniela de amigos, simple y brutal:
- 10 participantes con 3 equipos NFL cada uno, durante toda la temporada regular (18 semanas).
- Victoria de alguno de tus equipos: +1 punto. Derrota o empate: 0 puntos.
- Desempate: diferencia de puntos acumulada de tus tres equipos (PF − PA).
- La vista principal respeta el orden de inscripción y arriba se destacan los 3 primeros lugares por puntos acumulados.
| Participante | Equipos |
|---|---|
| Participante 1 | Chiefs, Dolphins, Cardinals |
| Participante 2 | Bills, Buccaneers, Saints |
| Participante 3 | Rams, Commanders, Colts |
| Participante 4 | Ravens, Jets, Vikings |
| Participante 5 | Lions, 49ers, Steelers |
| Participante 6 | Broncos, Chargers, Falcons |
| Participante 7 | Packers, Bengals, Titans |
| Participante 8 | Seahawks, Jaguars, Bears |
| Participante 9 | Eagles, Cowboys, Panthers |
| Participante 10 | Texans, Patriots, Raiders |
(Browns y Giants quedaron sin dueño. Alguien tendrá que explicar esa estrategia.)
La parte interesante: resultados en vivo, sin API key
Los resultados se consultan en la API pública de ESPN — no requiere clave ni registro. Pero hay un detalle: ESPN rechaza clientes HTTP “de biblioteca”. Cuando las peticiones vienen de requests o httpx, el servidor detecta el fingerprint TLS y responde como si la API no existiera.
La solución: curl_cffi, una librería que imita el handshake TLS de Chrome. Para el servidor de ESPN, mi backend es un navegador más.
Encima puse una caché en SQLite con TTL inteligente, pensada para el ritmo real de la NFL:
- Semanas ya terminadas → se refrescan cada 12 horas (los resultados no cambian).
- Semanas futuras → cada 24 horas (solo cambia la agenda).
- Semana activa → cada 90 segundos, para que los marcadores en vivo se actualicen solos mientras hay juegos.
Y si ESPN falla o se cae, se sirve la última caché buena: la app nunca se queda en blanco.
El stack
backend/ FastAPI: cliente ESPN + caché SQLite + cálculo de posiciones
frontend/ React + Vite + MUI (Material Design)
Dockerfile multi-stage: construye el frontend → imagen final liviana
El backend expone tres endpoints: /api/health (estado del servicio), /api/standings (posiciones completas, con ?refresh=1 para forzar refresco) y /api/weeks/{n} (juegos y puntos por participante de la semana 1 a la 18). La temporada es configurable con una variable de entorno (NFL_SEASON), así que el año que viene la app sigue sirviendo sin tocar código.
Accesible desde cualquier dispositivo
La app corre en Docker en un servidor de la red local: cualquier celular, tablet o computadora en la misma WiFi la abre por el puerto del contenedor. Pero los amigos no viven en mi red — y no voy a abrir puertos del router por una quiniela.
Para eso usé Tailscale Funnel: expone el servicio en internet con HTTPS y certificado válido, sin abrir ningún puerto de casa. Ese es el link que compartimos en el grupo — funciona igual con datos móviles o en una WiFi ajena.
Lo que me dejó el proyecto
Lo que empezó como “un script para no actualizar a mano” terminó siendo un mini producto: un cliente que sortea el anti-bot de ESPN, una caché con TTL por estado de la semana, fallback ante fallos, una API limpia, un frontend responsivo y un despliegue reproducible con Docker.
Es el mismo enfoque que aplico en proyectos de clientes: identificar la fricción, automatizar lo repetitivo y diseñar para el dispositivo real donde se usa la herramienta — en este caso, el teléfono en el sofá, un domingo por la tarde, con los juegos en vivo.
La próxima temporada arranca sola. La quiniela también.
← Volver al blog