No olvidar instalar la librería:
pip install tzdata
Crear endpoints dinámicos con FastAPI te permite devolver datos en tiempo real, como la hora del servidor según el país solicitado. Aprenderás a registrar rutas, tipar variables y trabajar con zonas horarias usando módulos nativos de Python, ideal para quienes construyen APIs con parámetros de ruta.
¿Cómo registrar un endpoint que devuelva la hora actual en FastAPI?
Todo empieza con una función simple que retorne la hora del servidor en el momento exacto de la ejecución. La idea es exponer esa función como un endpoint accesible desde el navegador.
Dentro del archivo sync defines una función llamada time que retorne la hora actual. Para conseguirlo, importas el módulo datetime desde la librería datetime y devuelves datetime.now(). Hasta aquí tienes la lógica, pero falta exponerla como ruta.
El registro se hace con el decorador @get, al que le pasas el path time. FastAPI recarga la aplicación automáticamente y, al visitar /time en el navegador, recibes un JSON con la hora actual del servidor.
¿Qué hace el decorador @get en FastAPI? Convierte una función de Python en un endpoint HTTP accesible mediante el método GET, asociándola a la ruta que le indiques entre paréntesis.
¿Cómo recibir parámetros de ruta dinámicos en un endpoint?
Un endpoint que no recibe variables es estático y poco útil en escenarios reales. FastAPI permite recibir variables directamente desde la URL usando llaves dentro del path registrado.
En el ejemplo se usa iso_code como variable de ruta para identificar el país del cual se quiere consultar la hora. Esa variable llega como argumento a la función, pero hay un detalle importante: hay que tiparla.
¿Por qué tipar las variables con str en Python?
Tipar significa indicarle al código de qué tipo es la variable. Si pasas el cursor sobre una variable sin tipar, el editor muestra any, lo que significa cualquier tipo, y no obtienes autocompletado.
Al declarar iso_code: str, le dices al editor que la variable es de tipo string. A partir de ahí, el editor sugiere métodos disponibles como upper(), que transforma el texto a mayúsculas.
Esto importa porque las llaves del diccionario de zonas horarias están en mayúsculas. Si el usuario escribe co, Co o CO, el método upper() normaliza la entrada y evita errores de coincidencia.
¿Cómo obtener la hora por país usando zoneinfo en Python?
Con la entrada normalizada, el siguiente paso es traducir el código del país a una zona horaria válida. Aquí entran un diccionario y el módulo zoneinfo de Python.
El diccionario contiene los time zones soportados, con llaves en mayúsculas como CO o MX. Usando el método .get() del diccionario, obtienes el string de la zona horaria correspondiente al código recibido.
Luego importas zoneinfo y creas un objeto de zona horaria pasándole ese string. Ese objeto se entrega como argumento a datetime.now(tz), que devuelve la hora ajustada al time zone solicitado.
El resultado:
CO, la respuesta llega con offset -5.MX, la respuesta llega con offset -6.upper().¿Qué es zoneinfo en Python? Es un módulo nativo que te permite trabajar con zonas horarias usando la base de datos IANA, sin instalar librerías externas como pytz.
¿Qué ventaja tienen los parámetros de ruta dinámicos?
Los parámetros de ruta permiten que un mismo endpoint responda con información distinta según la variable enviada. En lugar de crear un endpoint por país, uno solo cubre todos los casos.
Esa flexibilidad es la base para construir APIs escalables. Puedes aplicar la misma lógica para usuarios, productos, idiomas o cualquier recurso identificable por un código.
Como reto, crea un nuevo endpoint que reciba una variable en formato GET y permita activar el formato de hora de 24 horas cuando el usuario lo requiera. ¿Cómo lo resolverías tú? Cuéntame en los comentarios.
Crear endpoints dinámicos con FastAPI te permite devolver datos en tiempo real, como la hora del servidor según el país solicitado. Aprenderás a registrar rutas, tipar variables y trabajar con zonas horarias usando módulos nativos de Python, ideal para quienes construyen APIs con parámetros de ruta.
Todo empieza con una función simple que retorne la hora del servidor en el momento exacto de la ejecución. La idea es exponer esa función como un endpoint accesible desde el navegador.
Dentro del archivo sync defines una función llamada time que retorne la hora actual. Para conseguirlo, importas el módulo datetime desde la librería datetime y devuelves datetime.now(). Hasta aquí tienes la lógica, pero falta exponerla como ruta.
El registro se hace con el decorador @get, al que le pasas el path time. FastAPI recarga la aplicación automáticamente y, al visitar /time en el navegador, recibes un JSON con la hora actual del servidor.
¿Qué hace el decorador @get en FastAPI? Convierte una función de Python en un endpoint HTTP accesible mediante el método GET, asociándola a la ruta que le indiques entre paréntesis.
Un endpoint que no recibe variables es estático y poco útil en escenarios reales. FastAPI permite recibir variables directamente desde la URL usando llaves dentro del path registrado.
En el ejemplo se usa iso_code como variable de ruta para identificar el país del cual se quiere consultar la hora. Esa variable llega como argumento a la función, pero hay un detalle importante: hay que tiparla.
Tipar significa indicarle al código de qué tipo es la variable. Si pasas el cursor sobre una variable sin tipar, el editor muestra any, lo que significa cualquier tipo, y no obtienes autocompletado.
Al declarar iso_code: str, le dices al editor que la variable es de tipo string. A partir de ahí, el editor sugiere métodos disponibles como upper(), que transforma el texto a mayúsculas.
Esto importa porque las llaves del diccionario de zonas horarias están en mayúsculas. Si el usuario escribe co, Co o CO, el método upper() normaliza la entrada y evita errores de coincidencia.
Con la entrada normalizada, el siguiente paso es traducir el código del país a una zona horaria válida. Aquí entran un diccionario y el módulo zoneinfo de Python.
El diccionario contiene los time zones soportados, con llaves en mayúsculas como CO o MX. Usando el método .get() del diccionario, obtienes el string de la zona horaria correspondiente al código recibido.
Luego importas zoneinfo y creas un objeto de zona horaria pasándole ese string. Ese objeto se entrega como argumento a datetime.now(tz), que devuelve la hora ajustada al time zone solicitado.
El resultado:
CO, la respuesta llega con offset -5.MX, la respuesta llega con offset -6.upper().¿Qué es zoneinfo en Python? Es un módulo nativo que te permite trabajar con zonas horarias usando la base de datos IANA, sin instalar librerías externas como pytz.
Los parámetros de ruta permiten que un mismo endpoint responda con información distinta según la variable enviada. En lugar de crear un endpoint por país, uno solo cubre todos los casos.
Esa flexibilidad es la base para construir APIs escalables. Puedes aplicar la misma lógica para usuarios, productos, idiomas o cualquier recurso identificable por un código.
Como reto, crea un nuevo endpoint que reciba una variable en formato GET y permita activar el formato de hora de 24 horas cuando el usuario lo requiera. ¿Cómo lo resolverías tú? Cuéntame en los comentarios.
No olvidar instalar la librería:
pip install tzdata

En Windows puede dar error porque el SO no trae las zonas horarias por defecto; para que me funcionara, hice esto
Instalé tzdata
pip install tzdata
Y utilicé el siguiente código:
from fastapi import FastAPI, HTTPException
from datetime import datetime
import zoneinfo
@app.get("/time/{iso_code}")
async def get_time_by_iso(iso_code: str):
iso = iso_code.upper()
timezone_str = country_timezones.get(iso)
if not timezone_str:
raise HTTPException(
status_code=404,
detail=f"Código ISO {iso_code} no soportado"
)
try:
tz = zoneinfo.ZoneInfo(timezone_str)
data_time_24h = datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
return {
"iso": iso,
"timezone": timezone_str,
"time": data_time_24h
}
except zoneinfo.ZoneInfoNotFoundError:
raise HTTPException(
status_code=500,
detail="Zona horaria no disponible. Verifique tzdata."
)

antes de ejecutar instalar pip install tzdata
COUNTRY_ZONES = {
"NI": "America/Managua", # Nicaragua
"CO": "America/Bogota", # Colombia
"MX": "America/Mexico_City", # México
"AR": "America/Argentina/Buenos_Aires", # Argentina
"PE": "America/Lima", # Perú
"US": "America/New_York", # Estados Unidos
"ES": "Europe/Madrid", # España
"JP": "Asia/Tokyo", # Japón
"AU": "Australia/Sydney", # Australia
"BR": "America/Sao_Paulo" # Brasil
}
@app.get('/current-time/{iso_code}')
async def get_current_time_isocode(iso_code: str):
iso = iso_code.upper()
timezone_str = COUNTRY_ZONES.get(iso)
if not timezone_str:
return { "message": "Timezone does not exist." }
try:
timezone = zoneinfo.ZoneInfo(timezone_str)
except:
return { "message": "Something was wrong. We don't recognize the ISO code or currently is not available." }
return { iso_code: datetime.date(timezone) }
Para quienes estén en Windows, no olvidar instalar la librería tzdata
pip install tzdata
Me pasó que me sacaba un error en el servidor al ejecutar /time/(cualquier country_zone)
Una vez se instala tzdata, se resuelve ese problema.
country_timezones = {
"CO": "America/Bogota",
"US": "America/New_York",
"AR": "America/Argentina/Buenos_Aires",
"BR": "America/Sao_Paulo",
"MX": "America/Mexico_City",
"PE": "America/Lima",
"CL": "America/Santiago",
"VE": "America/Caracas",
}
format = {"12": "%I:%M:%S %p", "24": "%H:%M:%S"}
@app.get("/hour/{iso_code}/{time_format}")
async def time_hour(iso_code: str, time_format: str):
iso = iso_code.upper()
timezone_str = country_timezones.get(iso)
if timezone_str is None:
raise HTTPException(status_code=404, detail="codigo no valido")
tz = zoneinfo.ZoneInfo(timezone_str)
now = datetime.now(tz)
formatted_time = format.get(time_format, "%H:%M:%S")
return {"timezone": timezone_str, "time": now.strftime(formatted_time)}
En FastAPI, los parámetros de ruta y las consultas son fundamentales para manejar la entrada de datos HTTP de manera flexible. A continuación te explico cómo funcionan y cómo utilizarlos.
Los parámetros de ruta son aquellos que se pasan directamente en la URL. Se pueden definir usando corchetes {} dentro de las funciones app.get().
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}") async def read_item(item_id: int): return {"item_id": item_id}
Uso:
{item_id} es un parámetro de ruta que espera un valor entero (int).item_id que se pase en la URL será accesible como un argumento en la función./items/123{"item_id": 123}Los parámetros de consulta son aquellos que se incluyen después del signo de interrogación ? en la URL, y pueden tener varios pares clave-valor separados por &. Se pueden definir en la función app.get() usando el parámetro query.
from fastapi import FastAPI from typing import Optional
app = FastAPI()
@app.get("/search") async def search( q: Optional[str] = None, # Parámetro de consulta opcional limit: int = 10, # Parámetro de consulta con un valor por defecto offset: int = 0 ): return {"query": q, "limit": limit, "offset": offset}
Uso:
q es un parámetro de consulta que acepta un valor de tipo str, que puede ser opcional (Optional[str]).limit es un parámetro de consulta que define el número máximo de resultados, con un valor por defecto de 10.offset es otro parámetro de consulta que indica el número de resultados a saltar, con un valor por defecto de 0./search?q=fastapi&limit=5&offset=10{"query": "fastapi", "limit": 5, "offset": 10}Se pueden usar ambos al mismo tiempo en una misma ruta. FastAPI los maneja de manera ordenada y los puede distinguir fácilmente.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}") async def read_item(item_id: int, q: Optional[str] = None): return {"item_id": item_id, "query": q}
Uso:
item_id es el parámetro de ruta.q es el parámetro de consulta opcional./items/123?q=fastapi{"item_id": 123, "query": "fastapi"}En FastAPI puedes especificar el tipo de validación que deseas realizar en los parámetros utilizando las anotaciones de tipo (int, str, float, etc.) y los posibles valores por defecto, como se ha visto en los ejemplos anteriores.
app.get("/{param}").? en la URL y accesibles por medio de app.get("/route?param=value").Esto te permite manejar de manera flexible cómo los usuarios interactúan con tus APIs en función de sus necesidades.
Mi solucion al reto:
from fastapi import FastAPI # Libreria para crear APIs
from datetime import datetime # Libreria para manejar fechas y horas
import zoneinfo # Libreria para manejar zonas horarias
# Crear la aplicacion FastAPI
app = FastAPI()
# Diccionario de paises por codigo ISO
country = {
"CO": "Colombia",
"US": "Estados Unidos",
"AR": "Argentina",
"BR": "Brasil",
"MX": "Mexico",
"PE": "Peru",
"CL": "Chile",
"VE": "Venezuela",
}
# Diccionario de zonas horarias por codigo ISO de paises
country_timezones = {
"CO": "America/Bogota",
"US": "America/New_York",
"AR": "America/Argentina/Buenos_Aires",
"BR": "America/Sao_Paulo",
"MX": "America/Mexico_City",
"PE": "America/Lima",
"CL": "America/Santiago",
"VE": "America/Caracas",
}
# Formatos de fecha y hora
datetime_format = {
"24": "%Y-%m-%d %H:%M:%S",
"12": "%Y-%m-%d %I:%M:%S %p",
}
# Obtener la fecha y hora en una zona horaria y formato especifico
def get_time_in_timezone(iso_code: str, format_code: str = None):
try:
iso = iso_code.upper()
timezone_str = country_timezones.get(iso) if country_timezones.get(iso) else None
tz = zoneinfo.ZoneInfo(timezone_str)
if not format_code:
return {"datetime UTC": datetime.now(tz)}
else:
time_format = datetime_format.get(format_code)
return {
f"datetime {format_code}": country.get(iso) + " (" + iso + "), " + datetime.now(tz).strftime(time_format)
}
except Exception as e:
return {"error": f"Verify parameters => iso_code: {list(country_timezones.keys())} or format_code: {list(datetime_format.keys())}"}
# Endpoint raiz
@app.get("/")
async def root():
return {"message": "Hola, Luis!"}
# Endpoint para obtener la hora actual en UTC
@app.get("/time")
async def time():
return {"time": datetime.now()}
# Endpoint para obtener la hora actual en una zona horaria especifica
@app.get("/time/{iso_code}")
async def time(iso_code: str):
return get_time_in_timezone(iso_code)
# Endpoint para obtener la hora actual en una zona horaria y formato especificos
@app.get("/time/{iso_code}/{format_code}")
async def time(iso_code: str, format_code: str):
return get_time_in_timezone(iso_code, format_code)
Resultado

Reto:
@app.get("/current-time/{format}")
async def get_current_time(format: str):
available_formats = {
"24h": "%H:%M:%S",
"12h": "%I:%M:%S %p"
}
time_format = available_formats.get(format.lower())
if not time_format:
return {"error": "Unsupported format. Use '24h' or '12h'."}
current_time = now.strftime(time_format)
return {"current_time": current_time}