Conversor de monedas con Python: Requests y Decimal

Actualizado el: 21/08/2026
Tempo de leitura: 5 minutos
Conversor de moedas desenvolvido com Python

Un conversor de monedas es un proyecto excelente para practicar entrada de datos, funciones, solicitudes HTTP, validación y manejo de errores. La aplicación recibe un importe, una moneda de origen y otra de destino, consulta una tasa actual y devuelve el valor convertido. Aunque el cálculo es sencillo, un programa confiable debe gestionar códigos inválidos, fallos de red, datos incompletos y fechas de actualización.

Antes de comenzar, revisa la biblioteca Requests en Python, el manejo de excepciones, las funciones, las variables de entorno y el trabajo con diccionarios.

Cómo funciona la conversión

La fórmula básica es:

resultado = importe * tasa

Si un euro equivale a 1,08 dólares, convertir 100 EUR produce 108 USD. La dificultad está en obtener una tasa adecuada y saber qué representa. Algunas APIs devuelven tasas respecto a una moneda base; otras permiten solicitar directamente el par deseado.

El Banco Central Europeo publica tipos de referencia para varias monedas. Para un proyecto educativo también puede utilizarse la documentación de la API Frankfurter, que ofrece datos basados en fuentes públicas. Ninguna tasa de referencia garantiza el importe exacto que cobrará un banco, una tarjeta o una casa de cambio.

Preparar el entorno

python -m venv .venv
.venv\Scripts\activate
python -m pip install requests

En macOS o Linux activa el entorno con source .venv/bin/activate. Evita instalar dependencias globalmente, porque distintos proyectos pueden requerir versiones diferentes.

Primera consulta a la API

import requests

url = "https://api.frankfurter.app/latest"
parametros = {
    "amount": 100,
    "from": "EUR",
    "to": "USD",
}

respuesta = requests.get(url, params=parametros, timeout=10)
respuesta.raise_for_status()
datos = respuesta.json()
print(datos)

El parámetro timeout evita que el programa espere indefinidamente. raise_for_status() transforma respuestas HTTP de error en excepciones que pueden manejarse.

Extraer el resultado

resultado = datos["rates"]["USD"]
fecha = datos["date"]
print(f"100 EUR = {resultado:.2f} USD")
print(f"Tasa de referencia: {fecha}")

No asumas que todas las claves existen. Una respuesta de error puede tener una estructura distinta. Utiliza acceso seguro y valida tipos.

Crear una función reutilizable

from decimal import Decimal, InvalidOperation
import requests

API_URL = "https://api.frankfurter.app/latest"


def convertir_moneda(importe, origen, destino):
    origen = origen.strip().upper()
    destino = destino.strip().upper()

    if len(origen) != 3 or len(destino) != 3:
        raise ValueError("Los códigos deben tener tres letras")

    if origen == destino:
        return {
            "resultado": Decimal(str(importe)),
            "fecha": None,
            "origen": origen,
            "destino": destino,
        }

    parametros = {
        "amount": str(importe),
        "from": origen,
        "to": destino,
    }

    respuesta = requests.get(API_URL, params=parametros, timeout=10)
    respuesta.raise_for_status()
    datos = respuesta.json()

    tasas = datos.get("rates", {})
    if destino not in tasas:
        raise ValueError("La respuesta no contiene la moneda solicitada")

    return {
        "resultado": Decimal(str(tasas[destino])),
        "fecha": datos.get("date"),
        "origen": origen,
        "destino": destino,
    }

Decimal ayuda a conservar una representación decimal controlada. Aun así, la precisión final depende de la tasa proporcionada por el servicio.

Validar la entrada del usuario

def leer_importe():
    while True:
        texto = input("Importe: ").strip().replace(",", ".")
        try:
            valor = Decimal(texto)
            if valor < 0:
                print("El importe no puede ser negativo")
                continue
            return valor
        except InvalidOperation:
            print("Introduce un número válido")

En una aplicación real debes decidir si el valor cero es aceptable y qué límites aplicar. Los códigos de moneda deben compararse con una lista admitida, no solo por longitud.

Obtener las monedas disponibles

def obtener_monedas():
    respuesta = requests.get(
        "https://api.frankfurter.app/currencies",
        timeout=10,
    )
    respuesta.raise_for_status()
    datos = respuesta.json()
    if not isinstance(datos, dict):
        raise ValueError("Formato inesperado")
    return datos

Esta información puede almacenarse temporalmente porque no cambia con frecuencia. Así evitas una solicitud adicional en cada conversión.

Programa de terminal completo

def main():
    print("Conversor de monedas")
    importe = leer_importe()
    origen = input("Moneda de origen (EUR): ").strip().upper()
    destino = input("Moneda de destino (USD): ").strip().upper()

    try:
        datos = convertir_moneda(importe, origen, destino)
    except requests.Timeout:
        print("La consulta tardó demasiado")
        return
    except requests.ConnectionError:
        print("No hay conexión con el servicio")
        return
    except requests.HTTPError as error:
        print(f"El servicio devolvió un error: {error.response.status_code}")
        return
    except (ValueError, KeyError) as error:
        print(f"Datos inválidos: {error}")
        return

    resultado = datos["resultado"]
    print(
        f"{importe:.2f} {datos['origen']} = "
        f"{resultado:.2f} {datos['destino']}"
    )
    if datos["fecha"]:
        print(f"Fecha de referencia: {datos['fecha']}")


if __name__ == "__main__":
    main()

Calcular la tasa implícita

tasa = resultado / importe if importe else Decimal("0")
print(f"1 {origen} = {tasa:.6f} {destino}")

No calcules la tasa cuando el importe sea cero. La cantidad de decimales visible depende del uso: dos pueden ser suficientes para el resultado monetario, pero una tasa suele necesitar más.

Separar lógica, red e interfaz

Un diseño mantenible divide el proyecto:

conversor/
├── api.py
├── servicio.py
├── validacion.py
├── main.py
└── tests/

api.py consulta el servicio. servicio.py aplica reglas. validacion.py comprueba entradas. main.py conversa con el usuario. Esta separación permite probar el cálculo sin realizar solicitudes reales.

Usar caché

Si muchas personas consultan el mismo par, guarda la tasa durante un período corto:

from datetime import datetime, timedelta, timezone

cache = {}


def guardar_tasa(clave, valor):
    cache[clave] = {
        "valor": valor,
        "expira": datetime.now(timezone.utc) + timedelta(minutes=30),
    }

Antes de reutilizarla, confirma que no haya expirado. Una caché reduce llamadas y mantiene el programa funcional ante interrupciones breves, pero debes mostrar la fecha para no presentar un valor antiguo como actual.

Conversión sin conexión

Puedes guardar la última respuesta en JSON y usarla como respaldo:

import json
from pathlib import Path

RUTA_CACHE = Path("tasas_cache.json")


def guardar_cache(datos):
    RUTA_CACHE.write_text(
        json.dumps(datos, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )

Al utilizar datos guardados, informa claramente que son tasas anteriores. No ocultes la falta de conexión.

Claves de API y seguridad

Algunos proveedores requieren una clave. Nunca la escribas directamente en el repositorio:

import os

api_key = os.getenv("EXCHANGE_API_KEY")
if not api_key:
    raise RuntimeError("Falta EXCHANGE_API_KEY")

Usa variables de entorno, limita los permisos y rota la clave si se publica accidentalmente.

Probar sin depender de Internet

Simula la respuesta HTTP con un mock. Prueba un resultado correcto, timeout, código 500, JSON inválido, moneda ausente y origen igual a destino. También prueba importes con coma, valores negativos y números muy grandes.

Crear una interfaz gráfica

Cuando la versión de terminal funcione, puedes añadir Tkinter. La interfaz debe llamar a la misma función de conversión en lugar de duplicar el cálculo. Realiza la solicitud fuera del hilo principal para que la ventana no se congele y desactiva el botón mientras la consulta está en curso.

Limitaciones importantes

Las tasas de referencia no incluyen comisiones, margen comercial, impuestos ni diferencias entre compra y venta. Tampoco representan necesariamente cotizaciones en tiempo real. Muestra la fuente, la fecha y una advertencia cuando el resultado se utilice para planificación financiera.

Errores frecuentes

No hagas solicitudes sin timeout. No confíes ciegamente en el JSON. No uses una clave pública dentro del código. No redondees antes de terminar el cálculo y no confundas el símbolo con el código ISO. Evita consultar la API en cada pulsación del teclado y respeta los límites del proveedor.

Conclusión

Un conversor de monedas combina una operación sencilla con prácticas reales de software: validación, HTTP, JSON, precisión decimal, caché y manejo de fallos. Separa la lógica de la interfaz, utiliza una fuente confiable, conserva la fecha de referencia y comunica las limitaciones. Con esta base puedes crear una herramienta de terminal, una aplicación gráfica o un pequeño servicio web sin duplicar la lógica principal.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Búsqueda en PDF con Python y RAG
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Guía PDF con Python y RAG

    Aprende a consultar PDF con Python y RAG, recuperación semántica, evaluación y seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    23/07/2026
    Integração do ChatGPT com código Python em projeto prático
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    OpenAI API con Python: integración y seguridad

    Integra la API de OpenAI con Python usando Responses API, variables de entorno, validación, reintentos, control de costes, pruebas y

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Calendário mensal gerado automaticamente com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Calendario mensual en Python con calendar

    Genera un calendario mensual con Python usando calendar y datetime, valida fechas, añade eventos y exporta a TXT, HTML o

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Projeto de jogo da velha desenvolvido em Python como exercício de lógica de programação
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Tres en raya con Python: lógica y validación

    Crea un juego de tres en raya con Python usando listas, funciones, validación, detección de victoria, marcador, pruebas y una

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    texto Tkinter com uma interface gráfica dele ao lado
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Tkinter en Python: crea una app de escritorio

    Crea tu primera aplicación con Tkinter: ventanas, ttk, formularios, grid, eventos, archivos, threads, estilos y separación de lógica.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Sistema de lembretes e alarmes desenvolvido com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Recordatorios con alarma en Python: datetime y sonido

    Crea recordatorios con alarma en Python usando datetime, JSON, threads, zonas horarias, sonidos, recurrencia y pruebas controladas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026