Volver al blog

Cómo crear facturación multidivisa con una API de tipos de cambio (Guía 2026)

V
Vlado Grigirov
July 30, 2026
Multi-Currency Currency API Exchange Rates Invoicing Developer Guide Accounting

La facturación multidivisa parece un problema resuelto: eliges una moneda, multiplicas por un tipo de cambio e imprimes el total. En la práctica, es una de las áreas más propensas a errores de cualquier sistema de facturación, y los errores salen caros porque aparecen en tus cuentas por cobrar, en tus declaraciones fiscales y en las bandejas de entrada de tus clientes. Si eres un desarrollador que integra facturación multidivisa en un producto SaaS, una herramienta para autónomos, una plataforma de agencia o un marketplace B2B, la parte difícil no es la multiplicación. Es decidir qué tipo de cambio usar, cuándo fijarlo y cómo almacenarlo para que una factura que emitiste en marzo siga cuadrando correctamente cuando se pague en julio.

Esta guía recorre las decisiones de ingeniería que importan, con código ejecutable que usa una API de tipos de cambio para obtener, fijar y almacenar tasas. El foco está específicamente en la capa de divisas (FX), la parte que la mayoría de los tutoriales de facturación omiten.

Por qué la facturación multidivisa es más que conversión de moneda

Una factura en una sola moneda es una instantánea: cantidad por precio, más impuestos. Una factura multidivisa es un contrato sobre un momento en el tiempo. Cuando facturas a un cliente en EUR mientras tu contabilidad se lleva en USD, estás registrando una cuenta por cobrar cuyo valor en USD queda fijado el día en que la emites, aunque el tipo de mercado siga moviéndose hasta que el cliente realmente pague.

Esa brecha genera tres problemas concretos que la conversión simple ignora:

  1. Qué tasa aplica. ¿La del día de la factura, la del día del pago o la de "hoy"? Casi nunca coinciden, y las normas contables (tanto GAAP como IFRS) son específicas sobre la respuesta.
  2. Auditabilidad. Necesitas poder demostrar, meses después, exactamente qué tasa usaste y de dónde salió. "La obtuvimos de una API" no basta si no puedes reproducir el número.
  3. Ganancia y pérdida cambiaria. La diferencia entre el valor en la fecha de la factura y el valor en la fecha de pago es una ganancia o pérdida real que tiene que registrarse en algún lugar de tu libro mayor.

Si aciertas en esto, la facturación multidivisa resulta aburrida en el mejor sentido. Si te equivocas, tu equipo de finanzas se pasa la última semana de cada trimestre persiguiendo céntimos.

La regla de oro: fija el tipo de cambio en la fecha de la factura

La regla más importante de la facturación multidivisa es esta: congela el tipo de cambio en el momento en que se emite la factura y nunca lo recalcules.

Tanto GAAP como IFRS exigen registrar una transacción en moneda extranjera usando el tipo de contado vigente en la fecha de la transacción. Para una factura, la fecha de la transacción es la de emisión. Si facturas a un cliente el 1 de julio pero tu sistema no la procesa hasta el 5 de julio, sigues usando la tasa del 1 de julio. No es una regla de Finexly ni una preferencia: es la forma en que se establece legalmente el valor de la cuenta por cobrar en tu moneda funcional (local).

Un antipatrón habitual es mostrar un total convertido "en vivo" que cambia cada vez que el cliente actualiza la factura. Nunca hagas esto. Una factura es una reclamación fija por un importe específico. El cliente debe el importe en la moneda de la factura, y tu contabilidad debe un asiento a la tasa fijada. El mercado puede hacer lo que quiera después.

La conclusión práctica: la conversión de moneda para facturación es una operación de escritura única. Obtienes la tasa una vez, la almacenas junto con la factura y la tratas como inmutable durante toda la vida de ese documento.

Cómo elegir una API de tipos de cambio para facturación

No toda fuente de datos FX sirve para facturar. Para facturación quieres:

  • Cobertura de todas las monedas en las que facturas (Finexly cubre más de 170 monedas).
  • Una marca de tiempo "a fecha de" fiable en cada tasa, para poder demostrar qué cotización usaste.
  • Tasas históricas por fecha, porque las facturas retroactivas y corregidas son inevitables.
  • Límites de tasa y precios predecibles para que un pico de volumen de facturas no rompa la facturación. Revisa los planes de precios antes de crear una dependencia en tu flujo de cobro.

Una petición básica de tasa actual se ve así:

curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR&access_key=YOUR_API_KEY"

Y la respuesta te da una tasa más la marca de tiempo que almacenarás junto a ella:

{
  "success": true,
  "base": "USD",
  "timestamp": 1753660800,
  "rates": {
    "EUR": 0.9213
  }
}

Si quieres explorar las tasas de forma interactiva antes de conectar nada, el conversor de divisas es una comprobación rápida. Para una visión más amplia de cómo se compara Finexly con otros proveedores, consulta comparar APIs de divisas.

Obtener y fijar la tasa de la factura

Aquí tienes un pequeño ayudante en Python que obtiene la tasa de una factura y devuelve todo lo que necesitas persistir: la tasa, la marca de tiempo de origen y el total convertido. Fíjate en que devuelve la tasa y metadatos, no solo un número.

import os
import time
import requests
from decimal import Decimal, ROUND_HALF_UP

API_KEY = os.environ["FINEXLY_API_KEY"]
BASE_URL = "https://api.finexly.com/v1/latest"

def lock_invoice_rate(home_currency, invoice_currency):
    """Fetch and lock the FX rate to convert an invoice total
    (in invoice_currency) back into home_currency for the books."""
    resp = requests.get(BASE_URL, params={
        "base": invoice_currency,
        "symbols": home_currency,
        "access_key": API_KEY,
    }, timeout=10)
    resp.raise_for_status()
    data = resp.json()
    if not data.get("success"):
        raise RuntimeError("Rate lookup failed")

    rate = Decimal(str(data["rates"][home_currency]))
    return {
        "rate": rate,                       # invoice_currency -> home_currency
        "rate_base": invoice_currency,
        "rate_quote": home_currency,
        "source": "finexly",
        "as_of": data["timestamp"],         # store the source timestamp
        "locked_at": int(time.time()),      # when WE locked it
    }

def home_value(amount_invoice_ccy, rate):
    """Convert an invoice-currency amount into home currency."""
    return (Decimal(str(amount_invoice_ccy)) * rate).quantize(
        Decimal("0.01"), rounding=ROUND_HALF_UP
    )

El detalle crítico es que lock_invoice_rate se ejecuta una sola vez, al crear la factura, y su resultado se escribe en el registro de la factura. Para cuestiones de producción como el caché, los reintentos y el manejo elegante de respuestas 429, sigue los patrones de nuestra guía de caché y manejo de errores: no querrás que un fallo transitorio de la API bloquee la emisión de facturas.

Almacenar el tipo de cambio junto con la factura

Como la tasa es inmutable por factura, almacénala en la factura, no en una tabla compartida de "tasa actual" que podrías sobrescribir. Un esquema mínimo se ve así:

CREATE TABLE invoices (
  id              BIGSERIAL PRIMARY KEY,
  issue_date      DATE        NOT NULL,
  invoice_ccy     CHAR(3)     NOT NULL,   -- what the customer is billed in
  home_ccy        CHAR(3)     NOT NULL,   -- your functional currency
  total_invoice   NUMERIC(18,2) NOT NULL, -- total in invoice_ccy
  fx_rate         NUMERIC(18,8) NOT NULL, -- invoice_ccy -> home_ccy, LOCKED
  fx_source       TEXT        NOT NULL,   -- e.g. 'finexly'
  fx_as_of        TIMESTAMPTZ NOT NULL,   -- the rate's source timestamp
  total_home      NUMERIC(18,2) NOT NULL  -- total_invoice * fx_rate, at issue
);

Tres cosas hacen que este esquema sea apto para auditoría. Primero, fx_rate usa ocho decimales: las tasas necesitan mucha más precisión que los dos decimales de un importe monetario, y truncar antes de tiempo introduce desviaciones de redondeo. Segundo, fx_as_of registra de dónde salió el número y cuándo, de modo que cualquier auditor pueda reproducirlo contra datos históricos. Tercero, total_home se calcula y almacena en el momento de la emisión, así que un informe ejecutado seis meses después nunca tiene que adivinar.

Para transparencia con el cliente, imprime la tasa fijada en la propia factura: el importe adeudado, la moneda, la tasa usada y la fecha en que aplicó. Es una buena práctica ampliamente recomendada precisamente porque elimina la ambigüedad cuando el pago llega a una tasa distinta.

Facturas retroactivas: usa tasas históricas, no las de hoy

Tarde o temprano emitirás una factura con fecha pasada: una corrección, un asiento tardío o un contrato que especifica una fecha de efecto anterior. Usar la tasa de hoy para una factura de marzo es sencillamente incorrecto y no pasará una auditoría. En su lugar, obtén la tasa de la fecha real de la factura desde un endpoint histórico:

def lock_historical_rate(home_currency, invoice_currency, invoice_date):
    """invoice_date as 'YYYY-MM-DD'. Returns the locked rate for a
    backdated or corrected invoice."""
    resp = requests.get("https://api.finexly.com/v1/historical", params={
        "date": invoice_date,
        "base": invoice_currency,
        "symbols": home_currency,
        "access_key": API_KEY,
    }, timeout=10)
    resp.raise_for_status()
    data = resp.json()
    rate = Decimal(str(data["rates"][home_currency]))
    return {"rate": rate, "as_of": invoice_date, "source": "finexly"}

La regla es idéntica al caso en vivo —fijar una vez, almacenar para siempre— pero la fecha que consultas es la de emisión de la factura, no el día actual. Para un tratamiento más profundo del trabajo con tasas por fecha, consulta la guía de la API de tipos de cambio históricos.

Redondeo y precisión, bien hechos

Los errores con el dinero casi siempre son errores de redondeo. Dos reglas te mantienen a salvo.

Nunca uses coma flotante para el dinero. 0.1 + 0.2 no es 0.3 en aritmética de coma flotante, y esos errores diminutos se acumulan a lo largo de las líneas de la factura. Usa un tipo decimal: Decimal en Python, BigDecimal en Java, decimal en C#, o una representación de céntimos enteros en JavaScript.

Decide dónde ocurre el redondeo. Redondea la tasa a plena precisión (8+ decimales), pero redondea los importes a la unidad menor de la moneda: dos decimales para USD o EUR, cero decimales para JPY o KRW, tres para algunas otras. Un error frecuente es codificar dos decimales y emitir una factura por ¥1,234.56, que no es un importe válido en yenes. Determina el número de decimales a partir de la moneda, usando los datos de unidad menor de ISO 4217.

Para facturas con líneas de detalle, prefiere redondear cada línea y luego sumar, y concilia contra el total redondeado para que las líneas impresas sumen el total impreso. Sea cual sea la convención que elijas, aplícala de forma consistente en facturación, pagos e informes.

Reconocer la ganancia y pérdida cambiaria en el momento del pago

Aquí es donde la tasa fijada da sus frutos. Cuando el cliente paga, conviertes lo que realmente llegó a tu cuenta bancaria de vuelta a tu moneda local a la tasa de la fecha de pago. La diferencia entre eso y el valor local de la fecha de la factura es una ganancia o pérdida cambiaria realizada.

// Amounts kept as Decimal-like strings; use a money library in production.
function realizedFxGainLoss(invoice, paymentRate) {
  // invoice.totalInvoice: amount billed, in the invoice currency
  // invoice.fxRate:       LOCKED rate at issue (invoice_ccy -> home_ccy)
  // paymentRate:          rate on the day the payment settled

  const homeAtIssue   = invoice.totalInvoice * invoice.fxRate;
  const homeAtPayment = invoice.totalInvoice * paymentRate;

  const gainLoss = homeAtPayment - homeAtIssue;
  return {
    homeAtIssue:   round2(homeAtIssue),
    homeAtPayment: round2(homeAtPayment),
    fxGainLoss:    round2(gainLoss),        // > 0 gain, < 0 loss
  };
}

function round2(n) { return Math.round(n * 100) / 100; }

Obtienes paymentRate de la misma forma en que fijaste la tasa de la factura: una llamada latest en la fecha de liquidación, o una llamada historical si estás conciliando a posteriori. Contabiliza fxGainLoss en una cuenta dedicada de "ganancia/pérdida cambiaria". Ese único asiento es lo que mantiene tu contabilidad cuadrada cuando el mercado se mueve entre la emisión y el pago, y es exactamente el trabajo de conciliación que la facturación multidivisa manual hace mal. Las empresas que integran esto en flujos automatizados pueden apoyarse en la misma infraestructura de tasas descrita en nuestra guía de facturación SaaS multidivisa.

Notas de crédito, reembolsos y facturas recurrentes

Tres casos límite completan un sistema íntegro:

  • Las notas de crédito y los reembolsos deben revertir la factura original a su tasa fijada original, no a la tasa actual. Un reembolso es una reversión de la transacción inicial, así que reutiliza fx_rate de la factura que estás abonando. Cualquier diferencia residual en la fecha real del reembolso se convierte en otro pequeño asiento de ganancia/pérdida cambiaria.
  • Las facturas recurrentes obtienen cada una su propia tasa fijada en su propia fecha de emisión. Una suscripción de 12 meses facturada mensualmente produce doce facturas con doce tasas. No fijes una sola tasa para el año a menos que el contrato lo estipule explícitamente.
  • Las tasas fijadas por contrato a veces anulan el mercado: algunos contratos empresariales especifican una tasa fija para un período. Admite un campo de anulación manual, pero almacénalo con los mismos metadatos de auditoría para que siga siendo reproducible.

Preguntas frecuentes

¿Qué tipo de cambio debo usar en una factura? Usa el tipo de contado vigente en la fecha de emisión de la factura y luego fíjalo. Tanto GAAP como IFRS exigen registrar las transacciones en moneda extranjera a la tasa de la fecha de la transacción, y la fecha de la factura es esa fecha. No la recalcules cuando el cliente vea o pague la factura.

¿Debo almacenar el tipo de cambio o solo el importe convertido? Almacena ambos, más la fuente y la marca de tiempo de la tasa. Guardar solo el total convertido hace imposible auditar o calcular correctamente la ganancia/pérdida cambiaria más adelante. La tasa, la marca de tiempo "a fecha de" y la fuente juntas permiten que cualquiera reproduzca el número.

¿Cómo manejo una factura con fecha pasada? Consulta una tasa histórica para la fecha de emisión real en lugar de usar la de hoy. La regla de fijar una vez y almacenar para siempre es la misma; solo cambia la fecha que consultas.

¿Qué causa la ganancia o pérdida cambiaria en una factura? El movimiento del mercado entre la fecha de la factura y la fecha de pago. Tu contabilidad registró la cuenta por cobrar a la tasa de la fecha de la factura, pero el efectivo llega valorado a la tasa de la fecha de pago. La diferencia es una ganancia o pérdida cambiaria realizada y se contabiliza en una cuenta dedicada del libro mayor.

¿Necesito una API de divisas de pago para construir facturación multidivisa? Puedes empezar en un plan gratuito. La API gratuita de tipos de cambio de Finexly cubre tasas en vivo e históricas para volúmenes iniciales, y solo actualizas a medida que crece tu volumen de facturas.

Empieza ahora

La facturación multidivisa se reduce a una disciplina: fija la tasa en la fecha de la factura, almacénala con metadatos de auditoría completos y concilia la diferencia en el pago. Haz eso y la facturación internacional dejará de ser una carrera contrarreloj de fin de trimestre.

¿Listo para construirlo? Consigue tu clave API gratuita de Finexly: sin tarjeta de crédito. Empieza con tasas en vivo e históricas para más de 170 monedas en el plan gratuito y escala a medida que crece tu volumen de facturas.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →