Factureren in meerdere valuta's klinkt als een opgelost probleem: kies een valuta, vermenigvuldig met een wisselkoers en druk het totaal af. In de praktijk is het een van de meest foutgevoelige onderdelen van elk facturatiesysteem, en de fouten zijn duur omdat ze opduiken in je debiteuren, je belastingaangiften en de inboxen van je klanten. Als je als ontwikkelaar factureren in meerdere valuta's inbouwt in een SaaS-product, een tool voor freelancers, een bureauplatform of een B2B-marktplaats, is het lastige niet de vermenigvuldiging. Het lastige is beslissen welke wisselkoers je gebruikt, wanneer je die vastzet en hoe je die opslaat, zodat een factuur die je in maart uitschreef nog steeds correct aansluit wanneer die in juli wordt betaald.
Deze gids loopt door de technische beslissingen die ertoe doen, met uitvoerbare code die een wisselkoers-API gebruikt om koersen op te halen, vast te zetten en op te slaan. De focus ligt specifiek op de valutalaag (FX) — het deel dat de meeste facturatietutorials overslaan.
Waarom factureren in meerdere valuta's meer is dan valutaconversie
Een factuur in één valuta is een momentopname: aantal maal prijs, plus btw. Een factuur in meerdere valuta's is een contract over een moment in de tijd. Wanneer je een klant in EUR factureert terwijl je boeken in USD worden gevoerd, boek je een vordering waarvan de USD-waarde wordt vastgelegd op de dag dat je die uitschrijft — ook al blijft de marktkoers bewegen totdat de klant daadwerkelijk betaalt.
Die kloof creëert drie concrete problemen die eenvoudige conversie negeert:
- Welke koers geldt. De koers op de factuurdatum, de betaaldatum of van "vandaag"? Ze zijn vrijwel nooit gelijk, en de boekhoudkundige standaarden (zowel GAAP als IFRS) zijn specifiek over het antwoord.
- Controleerbaarheid. Je moet maanden later precies kunnen aantonen welke koers je gebruikte en waar die vandaan kwam. "We haalden hem van een API" volstaat niet als je het getal niet kunt reproduceren.
- Valutawinst en -verlies. Het verschil tussen de waarde op de factuurdatum en de waarde op de betaaldatum is een echte winst of verlies die ergens in je grootboek moet belanden.
Doe dit goed en factureren in meerdere valuta's wordt saai in de beste zin. Doe het fout en je financiële team besteedt de laatste week van elk kwartaal aan het najagen van centen.
De gouden regel: zet de wisselkoers vast op de factuurdatum
De belangrijkste regel bij factureren in meerdere valuta's is deze: bevries de wisselkoers op het moment dat de factuur wordt uitgeschreven en herbereken die nooit.
Zowel GAAP als IFRS vereisen dat je een transactie in vreemde valuta vastlegt tegen de spotkoers die geldt op de transactiedatum. Voor een factuur is de transactiedatum de uitgiftedatum. Als je een klant op 1 juli factureert maar je systeem die pas op 5 juli verwerkt, gebruik je toch de koers van 1 juli. Dit is geen Finexly-regel of voorkeur — zo wordt de waarde van de vordering in je functionele (thuis)valuta wettelijk vastgesteld.
Een veelvoorkomend antipatroon is het tonen van een "live" omgerekend totaal dat verandert telkens wanneer de klant de factuur vernieuwt. Doe dat nooit. Een factuur is een vaste vordering voor een specifiek bedrag. De klant is het bedrag verschuldigd in de factuurvaluta, en je boeken zijn een boeking verschuldigd tegen de vastgezette koers. De markt mag daarna doen wat hij wil.
De praktische conclusie: valutaconversie voor facturatie is een eenmalige schrijfoperatie. Je haalt de koers één keer op, slaat die op bij de factuur en behandelt die als onveranderlijk gedurende de hele levensduur van dat document.
Een wisselkoers-API kiezen voor facturatie
Niet elke FX-databron is geschikt voor facturatie. Voor facturatie wil je:
- Dekking van elke valuta waarin je factureert (Finexly dekt meer dan 170 valuta's).
- Een betrouwbare "per"-tijdstempel op elke koers, zodat je kunt aantonen welke notering je gebruikte.
- Historische koersen per datum, want met terugwerkende kracht en gecorrigeerde facturen zijn onvermijdelijk.
- Voorspelbare aanvraaglimieten en prijzen, zodat een piek in factuurvolume de facturatie niet breekt. Bekijk de prijsplannen voordat je een afhankelijkheid in je betaalpad inbouwt.
Een basale aanvraag voor de actuele koers ziet er zo uit:
curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR&access_key=YOUR_API_KEY"En de respons geeft je een koers plus de tijdstempel die je ernaast opslaat:
{
"success": true,
"base": "USD",
"timestamp": 1753660800,
"rates": {
"EUR": 0.9213
}
}Wil je de koersen interactief verkennen voordat je iets aansluit, dan is de valutaomrekenaar een snelle controle. Voor een breder beeld van hoe Finexly zich verhoudt tot andere providers, zie vergelijk valuta-API's.
De factuurkoers ophalen en vastzetten
Hier is een kleine Python-helper die de koers voor een factuur ophaalt en alles teruggeeft wat je moet bewaren: de koers, de brontijdstempel en het omgerekende totaal. Merk op dat hij de koers en metadata teruggeeft — niet alleen een getal.
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
)Het cruciale detail is dat lock_invoice_rate één keer draait, bij het aanmaken van de factuur, en dat de uitvoer naar het factuurrecord wordt geschreven. Voor productiezaken zoals caching, herpogingen en het net afhandelen van 429-responses volg je de patronen in onze gids voor caching en foutafhandeling — je wilt niet dat een tijdelijke API-hapering het uitschrijven van facturen blokkeert.
De wisselkoers opslaan bij de factuur
Omdat de koers per factuur onveranderlijk is, sla je die op bij de factuur, niet in een gedeelde tabel "actuele koers" die je zou kunnen overschrijven. Een minimaal schema ziet er zo uit:
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
);Drie dingen maken dit schema auditvriendelijk. Ten eerste gebruikt fx_rate acht decimalen — wisselkoersen hebben veel meer precisie nodig dan de twee decimalen van een geldbedrag, en te vroeg afkappen introduceert afrondingsdrift. Ten tweede legt fx_as_of vast waar het getal vandaan kwam en wanneer, zodat elke auditor het kan reproduceren tegen historische data. Ten derde wordt total_home berekend en opgeslagen op het moment van uitgifte, zodat een rapport dat zes maanden later draait nooit hoeft te gissen.
Voor transparantie naar de klant druk je de vastgezette koers op de factuur zelf af: het verschuldigde bedrag, de valuta, de gebruikte koers en de datum waarop die gold. Dit is een breed aanbevolen best practice, juist omdat het de dubbelzinnigheid wegneemt wanneer de betaling tegen een andere koers binnenkomt.
Facturen met terugwerkende kracht: gebruik historische koersen, niet die van vandaag
Vroeg of laat schrijf je een factuur uit met een datum in het verleden — een correctie, een late boeking of een contract dat een eerdere ingangsdatum vermeldt. De koers van vandaag gebruiken voor een maartfactuur is simpelweg fout en zal een audit niet doorstaan. Haal in plaats daarvan de koers voor de werkelijke factuurdatum op via een historisch eindpunt:
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"}De regel is identiek aan het live-geval — één keer vastzetten, voor altijd opslaan — maar de datum die je opvraagt is de uitgiftedatum van de factuur, niet de huidige dag. Voor een diepere behandeling van het werken met gedateerde koersen, zie de gids voor de historische wisselkoers-API.
Afronding en precisie, goed gedaan
Geldbugs zijn bijna altijd afrondingsbugs. Twee regels houden je uit de problemen.
Gebruik nooit drijvende komma voor geld. 0.1 + 0.2 is geen 0.3 in drijvendekomma-rekenkunde, en die minuscule fouten stapelen zich op over factuurregels. Gebruik een decimaal type: Decimal in Python, BigDecimal in Java, decimal in C#, of een representatie in hele centen in JavaScript.
Beslis waar de afronding plaatsvindt. Rond de koers af op volledige precisie (8+ decimalen), maar rond bedragen af op de kleinste eenheid van de valuta — twee decimalen voor USD of EUR, nul decimalen voor JPY of KRW, drie voor sommige andere. Een veelgemaakte fout is twee decimalen hardcoden en een factuur van ¥1,234.56 uitschrijven, wat geen geldig yenbedrag is. Leid het aantal decimalen af uit de valuta, met behulp van de ISO 4217-gegevens over de kleinste eenheid.
Voor facturen met regelposten kun je beter elke regel afronden en dan optellen, en aansluiten op het afgeronde totaal zodat de afgedrukte regels optellen tot het afgedrukte totaal. Welke conventie je ook kiest, pas die consistent toe over facturatie, betalingen en rapportage.
Valutawinst en -verlies herkennen op het moment van betaling
Hier betaalt de vastgezette koers zich uit. Wanneer de klant betaalt, reken je wat er daadwerkelijk op je bankrekening binnenkwam terug naar je thuisvaluta tegen de koers van de betaaldatum. Het verschil tussen dat en de thuiswaarde op de factuurdatum is een gerealiseerde valutawinst of -verlies.
// 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; }Je haalt paymentRate op dezelfde manier op als waarop je de factuurkoers vastzette — een latest-aanroep op de afwikkelingsdatum, of een historical-aanroep als je achteraf aansluit. Boek fxGainLoss op een aparte rekening "valutawinst/-verlies". Deze enkele boeking houdt je boeken in evenwicht wanneer de markt beweegt tussen uitgifte en betaling, en het is precies het aansluitwerk dat handmatig factureren in meerdere valuta's verkeerd doet. Bedrijven die dit in geautomatiseerde pijplijnen bouwen, kunnen leunen op dezelfde koersinfrastructuur die is beschreven in onze gids voor SaaS-facturatie in meerdere valuta's.
Creditnota's, terugbetalingen en terugkerende facturen
Drie randgevallen maken een compleet systeem af:
- Creditnota's en terugbetalingen moeten de oorspronkelijke factuur terugdraaien tegen de oorspronkelijk vastgezette koers — niet tegen de actuele koers. Een terugbetaling is een terugdraaiing van de oorspronkelijke transactie, dus hergebruik
fx_ratevan de factuur die je crediteert. Een eventueel restverschil op de werkelijke terugbetaaldatum wordt weer een kleine boeking valutawinst/-verlies. - Terugkerende facturen krijgen elk hun eigen vastgezette koers op hun eigen uitgiftedatum. Een abonnement van 12 maanden dat maandelijks wordt gefactureerd, levert twaalf facturen op met twaalf koersen. Zet geen enkele koers vast voor het jaar tenzij het contract die uitdrukkelijk vastlegt.
- Contractueel vastgezette koersen overrulen soms de markt — sommige zakelijke contracten specificeren een vaste koers voor een periode. Ondersteun een veld voor handmatige overschrijving, maar sla het op met dezelfde auditmetadata zodat het reproduceerbaar blijft.
Veelgestelde vragen
Welke wisselkoers moet ik op een factuur gebruiken? Gebruik de spotkoers die geldt op de uitgiftedatum van de factuur en zet die dan vast. Zowel GAAP als IFRS vereisen dat transacties in vreemde valuta worden vastgelegd tegen de koers van de transactiedatum, en de factuurdatum is die datum. Herbereken die niet wanneer de klant de factuur bekijkt of betaalt.
Moet ik de wisselkoers opslaan of alleen het omgerekende bedrag? Sla beide op — plus de bron en tijdstempel van de koers. Alleen het omgerekende totaal bewaren maakt het onmogelijk om later te auditen of de valutawinst/-verlies correct te berekenen. De koers, de "per"-tijdstempel en de bron samen laten iedereen het getal reproduceren.
Hoe behandel ik een factuur met een datum in het verleden? Vraag een historische wisselkoers op voor de werkelijke uitgiftedatum in plaats van die van vandaag te gebruiken. De regel één keer vastzetten, voor altijd opslaan is hetzelfde; alleen de datum die je opzoekt verandert.
Wat veroorzaakt valutawinst of -verlies op een factuur? De markt die beweegt tussen de factuurdatum en de betaaldatum. Je boeken legden de vordering vast tegen de koers van de factuurdatum, maar het geld komt binnen gewaardeerd tegen de koers van de betaaldatum. Het verschil is een gerealiseerde valutawinst of -verlies en wordt geboekt op een aparte grootboekrekening.
Heb ik een betaalde valuta-API nodig om factureren in meerdere valuta's te bouwen? Je kunt beginnen met een gratis abonnement. De gratis wisselkoers-API van Finexly dekt live en historische koersen voor volumes in de beginfase, en je upgradet pas naarmate je factuurdoorvoer groeit.
Aan de slag
Factureren in meerdere valuta's komt neer op één discipline: zet de koers vast op de factuurdatum, sla die op met volledige auditmetadata en sluit het verschil aan bij de betaling. Doe dat, en internationale facturatie is niet langer een brandje blussen aan het einde van het kwartaal.
Klaar om het te bouwen? Haal je gratis Finexly API-sleutel — geen creditcard nodig. Begin met live en historische koersen voor meer dan 170 valuta's op het gratis plan en schaal op naarmate je factuurvolume groeit.
Explore More
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 →