GuíasTutoriales

Cómo obtener tipos de cambio en Python

Empieza con la biblioteca estándar, usa requests cuando necesites más control y considera forex-python como paso de migración si ya dependes de esa interfaz.

1 oct 2026 · 6 min de lectura

Una petición a /v1/latest/USD devuelve JSON con el tipo, su origen y la hora de observación. Usa Decimal para no perder precisión al multiplicar importes.

  • urllib funciona sin dependencias adicionales.
  • requests simplifica autenticación, timeout y errores.
  • forex-python puede ser un adaptador temporal durante una migración.
  • Guarda source, effective_at y data_updated_at junto al valor.
  • Para históricos, usa una fecha concreta o /v1/range con paginación.

Tu primera llamada, sin dependencias

No necesitas nada fuera de la biblioteca estándar. urllib.request resuelve una llamada suelta:

Eso corre sin clave. Las llamadas anónimas tienen tope por dirección IP, así que para cualquier cosa más allá de una prueba rápida quieres una clave gratuita.

Con requests y una clave de API

La mayoría de proyectos ya usan requests. Pasa la clave como token bearer:

Una clave gratuita sube tu límite a 5,000 llamadas al mes a 10 peticiones por minuto. La cabecera X-API-Key también funciona, para plataformas que no pueden fijar Authorization.

Convierte un importe

Para convertir en vez de listar tipos, llama a /v1/convert. Devuelve el tipo y el importe convertido juntos:

Sabe cómo de fresco es un tipo

Para la moneda que muestras, consulta sources[currency] y effective_at[currency]: indican el tipo de fuente y la hora de observación. source resume los datos menos recientes de toda la respuesta. market_session describe la sesión de mercado; timestamp es la hora de generación de la respuesta, no la hora de observación.

Valor del campoQué significaCuándo se mueve
source: liveTipo spot indicativoIntradía (~60 s), semana de negociación
source: ecb_dailyFix de referencia oficialUna vez por día hábil
source: market_dailyCierre guardado sin BCEFeed de mercado diario
market_session: weekendSábado o domingo (interbancario cerrado)El fix de referencia arrastra el valor del viernes; el feed en vivo no se actualiza
Indicativos, no para liquidación

Los tipos se publican para referencia, análisis y visualización. No son una cotización en firme y no deben usarse para liquidar una operación. Cada respuesta lo lleva en su campo notice.

Lee un día pasado concreto

Desde 1999 con clave Plus/Pro; Free cubre 30 días. La respuesta añade is_forward_filled, que es true cuando ese día no se publicó tipo, como un fin de semana o un festivo:

Para una serie completa entre dos fechas en una sola llamada, usa /v1/range con start_date y end_date. Hay una guía aparte que lo cubre.

Migra desde forex-python

forex-python puede servir como una interfaz conocida, pero usa su propio proveedor y no se conecta automáticamente a exchangerate.dev. Al cambiar de proveedor, conserva el origen y la hora de observación de exchangerate.dev en tu modelo.

python · standard library
import jsonfrom decimal import Decimalimport urllib.requesturl = "https://api.exchangerate.dev/v1/latest/USD?symbols=EUR,GBP"with urllib.request.urlopen(url, timeout=10) as response:    data = json.load(response, parse_float=Decimal)print(data["rates"]["EUR"])print(data["sources"]["EUR"], data["effective_at"]["EUR"])print(data["market_session"])
python · requests
# Install first: python -m pip install requestsimport osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/latest/USD",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if response.status_code == 429:    raise RuntimeError(f"Rate limited; Retry-After: {response.headers.get('Retry-After')}")if 300 <= response.status_code < 400:    raise RuntimeError("Unexpected redirect")response.raise_for_status()data = response.json(parse_float=Decimal)print(data["rates"]["GBP"], data["effective_at"]["GBP"])
python · convert 100 USD to EUR
import osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/convert/USD/EUR/100",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if not 200 <= response.status_code < 300:    raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rate"], data["converted"])
python · rate on a past date
import osfrom decimal import Decimalimport requestsresponse = requests.get(    "https://api.exchangerate.dev/v1/2026-03-14/USD?symbols=JPY",    headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"},    timeout=10,    allow_redirects=False,)if not 200 <= response.status_code < 300:    raise RuntimeError(f"HTTP {response.status_code}; Retry-After: {response.headers.get('Retry-After')}")data = response.json(parse_float=Decimal)print(data["rates"]["JPY"], data["is_forward_filled"])

Preguntas

¿Necesito una clave de API para obtener tasas en Python?

No. Puedes llamar a /v1/latest/{base} de forma anónima para empezar. Las llamadas anónimas están limitadas por dirección IP, así que para producción conviene registrarse y obtener una clave gratuita, que sube el límite a 5,000 llamadas al mes a 10 peticiones por minuto.

¿Cómo paso mi clave de API en una petición de Python?

Envíala como token bearer en la cabecera Authorization, por ejemplo {"Authorization": "Bearer exr_live_..."}. También se acepta una cabecera X-API-Key para plataformas que no pueden fijar Authorization.

¿Qué monedas devuelve la API?

Una sola llamada a /v1/latest devuelve tipos para 144 monedas contra la base que elijas. También puedes convertir entre cualquier par soportado con /v1/convert, incluidos los cruces triangulados.

¿Sirven los tipos para ejecutar operaciones?

No. Los tipos son indicativos, publicados para referencia, análisis y visualización. No son una cotización en firme. Cada respuesta lo dice en su campo notice.

Sigue leyendo

Empieza con una clave API gratuita

5.000 solicitudes al mes con el plan gratuito. Sin tarjeta de crédito.

Obtén una clave gratuita