Aprender/Inicio rápido con Python

Cómo obtener tipos de cambio en Python

Lee tipos de cambio en vivo e históricos en Python con unas pocas líneas. Sin clave para empezar, 10,000 llamadas al mes al registrarte, y cada respuesta te dice cómo de fresco es el tipo.

ERexchangerate.dev·Jun 19, 2026·5 min de lectura

El camino más corto a los tipos de cambio en Python es una petición GET a /v1/latest/USD. Recibes JSON con tipos para 31 monedas más dos campos de frescura, source y market_session. Tu primera llamada no necesita clave de API.

Puntos clave
Un GET a /v1/latest/{base} devuelve tipos para 31 monedas en JSON.
Cada respuesta lleva source y market_session, así que sabes cómo de fresco es cada tipo.
El plan gratuito son 10,000 llamadas al mes a 12 peticiones por minuto, sin tarjeta.
Los tipos son indicativos, publicados para referencia y análisis, no como cotización en firme.

Tu primera llamada, sin dependencias

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

python · standard librarycopy
import json
from decimal import Decimal
import urllib.request

url = "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"])

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:

python · requestscopy
# Install first: python -m pip install requests
import os
from decimal import Decimal
import requests

response = 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"])

Una clave gratuita sube tu límite a 10,000 llamadas al mes a 12 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:

python · convert 100 USD to EURcopy
import os
from decimal import Decimal
import requests

response = 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"])

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: liveConsenso spot agregadoIntradía (~60 s), semana de negociación
source: ecb_dailyFix de referencia oficialUna vez por día hábil
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

Pide cualquier fecha hasta 1999 poniéndola en la ruta. 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:

python · rate on a past datecopy
import os
from decimal import Decimal
import requests

response = 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"])

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.

ER
exchangerate.dev
Guías de integración para desarrolladores.

Sigue leyendo

TutorialConversión de divisas en JavaScript y Node.jsLeer
Más ComparacionesFixer vs exchangerate.devOpen Exchange Rates vs exchangerate.devCurrencylayer vs exchangerate.dev
AprenderConversión de divisas en JavaScript y Node.jsTipos de cambio en Next.js y TypeScriptCrear una alerta de tipo de cambio en Python con USD/IDR
Tasas en VivoEUR/USDGBP/USDUSD/JPY