Terug naar Blog

Wisselkoersen voor belastingaangifte: een developersgids voor IRS-, HMRC- en EU-btw-omrekening

V
Vlado Grigirov
August 20, 2026
Currency API Exchange Rates Historical Rates Tax Reporting VAT Accounting Finexly

Elk systeem met meerdere valuta's komt vroeg of laat een accountant tegen. Die stelt een vraag die triviaal klinkt maar dat niet is: "Welke koers hebben jullie voor deze factuur gebruikt?" Als het eerlijke antwoord luidt "wat de API die middag teruggaf, en we hebben het niet bewaard", dan heb je een probleem dat geen hoeveelheid schone code nog oplost op het moment van aangifte.

Wisselkoersen voor belastingaangifte goed regelen gaat minder over het kiezen van de juiste koers — de meeste autoriteiten zijn daar verrassend soepel in — en veel meer over het jaren later kunnen aantonen welke koers je hebt gebruikt, waar die vandaan kwam en dat je dezelfde regel op elke andere transactie in de periode hebt toegepast. Dat is een datamodelleringsvraagstuk, en dat is precies het deel waar niemand over schrijft.

Deze gids behandelt wat de IRS, HMRC en de EU-btw-richtlijn werkelijk eisen, de vijf omrekenfouten die uitmonden in herzieningen, en een rate-snapshot-schema dat je deze week kunt implementeren.

Niemand is het eens over "de" wisselkoers — en dat is nu juist het punt

De nuttigste zin in dit hele domein komt van de IRS zelf:

"De Internal Revenue Service hanteert geen officiële wisselkoers. In het algemeen accepteert zij elke gepubliceerde wisselkoers die consistent wordt gebruikt."

Lees dat twee keer, want dezelfde vorm duikt in vrijwel elke jurisdictie op. De verplichting luidt zelden gebruik dit specifieke getal. Ze luidt: gebruik een verdedigbare bron, en gebruik die consistent. Consistentie is een eigenschap van jouw systeem, niet van je koersleverancier. Als je code in het weekend stilletjes terugvalt op een andere bron, heb je de eis geschonden zonder ooit een verkeerd getal te hebben opgehaald.

Verenigde Staten: spotkoers als standaard, jaargemiddelde als tegemoetkoming

Het uitgangspunt van de IRS is de spotkoers: "Gebruik in het algemeen de geldende wisselkoers (dat wil zeggen de spotkoers) op het moment waarop je de post ontvangt, betaalt of toerekent." Waar inkomsten gestaag aangroeien — salaris, huur, doorlopende bedrijfsomzet — publiceert de IRS een tabel met jaargemiddelde wisselkoersen en instrueert zij aangevers om "het bedrag in vreemde valuta te delen door de toepasselijke jaargemiddelde wisselkoers." Volgens de laatste update van die pagina op 24 februari 2026 beslaat de tabel de belastingjaren 2021 tot en met 2025.

Let op de richting van die bewerking. De IRS-tabel is genoteerd in eenheden vreemde valuta per één Amerikaanse dollar, dus je deelt. Draai je die per ongeluk om, dan zit je er niet een beetje naast — je zit er het kwadraat van de koers naast. Bij een yenbedrag is dat ruwweg vier ordes van grootte. Verderop meer over koersrichting, want het is veruit de meest voorkomende integratiefout in dit domein.

Voor de rapportage van Amerikaanse federale instanties bestaat een tweede officiële reeks: de Treasury Reporting Rates of Exchange, per kwartaal gepubliceerd op FiscalData.Treasury.gov in CSV, JSON en XML. Treasury omschrijft die als een weergave van de "wisselkoersen waartegen de Amerikaanse overheid vreemde valuta kan verwerven voor officiële uitgaven, zoals gerapporteerd door de betaalfunctionarissen van elke post op de laatste werkdag van de maand voorafgaand aan de datum van het gepubliceerde rapport." Wijken actuele koersen 10% of meer af van een gepubliceerde koers, dan geeft Treasury midden in het kwartaal een wijziging uit. De Fiscal Data API is open en vereist geen account of token — goed om te weten als je een referentiereeks van overheidsherkomst nodig hebt om tegen af te stemmen.

Verenigd Koninkrijk: VAT Notice 700 §7.6 heeft kracht van wet

Het Verenigd Koninkrijk is dwingender, en de betreffende tekst heeft wettelijke kracht op grond van Schedule 6, paragraaf 11 van de VAT Act 1994. VAT Notice 700 geeft bedrijven drie routes om leveringen in vreemde valuta naar pond sterling om te rekenen:

  1. De Britse marktverkoopkoers op het tijdstip van de levering. Dit is de standaard. De Notice stelt dat "de in landelijke dagbladen gepubliceerde koersen aanvaardbaar zijn als bewijs van de koersen op het relevante tijdstip."
  2. De HMRC-periodewisselkoers, gepubliceerd voor douanedoeleinden. Je mag die toepassen "op al je leveringen of op alle leveringen van een bepaalde soort of omschrijving." Voorafgaande melding is niet nodig — maar "nadat je een dergelijke keuze hebt gemaakt, kun je deze niet meer wijzigen zonder eerst schriftelijke instemming te verkrijgen van het VAT Written Enquiries Team."
  3. Een eigen commerciële koers of methode, waarvoor een schriftelijk verzoek nodig is. HMRC weegt af of de koers "wordt bepaald onder verwijzing naar de Britse valutamarkt", of die "objectief verifieerbaar" is en hoe vaak die wordt geactualiseerd. Cruciaal: "termijnkoersen of methoden die van termijnkoersen zijn afgeleid, zijn niet aanvaardbaar" — een harde grens die je het best begrijpt naast het verschil tussen spot- en termijnkoersen.

En de zin die je cachinglaag regeert: "Welke koers of methode je ook hanteert, de passende koers voor een levering is die welke geldt op het tijdstip van de levering." Tijdstip van levering — niet tijdstip van facturering, niet tijdstip van betaling, en al helemaal niet het tijdstip van je nachtelijke batchjob.

Kies je route 2, dan is de mechaniek aangenaam machinevriendelijk. HMRC publiceert maandkoersen op de voorlaatste donderdag van elke maand; ze gelden voor de volgende kalendermaand en geven de koersen weer per twaalf uur 's middags op de dag vóór publicatie. De bestanden staan op een voorspelbare URL — let erop dat de maand geen voorloopnul heeft:

https://www.trade-tariff.service.gov.uk/exchange_rates/view/files/monthly_csv_2026-9.csv
https://www.trade-tariff.service.gov.uk/exchange_rates/view/files/monthly_xml_2026-9.xml

Eén ophaalactie per maand, voor die maand gecachet, en elke Britse btw-omrekening in die periode is reproduceerbaar vanuit een bestand dat je aan een inspecteur kunt overhandigen.

Europese Unie: Artikel 91 van de btw-richtlijn

Voor intra-EU-leveringen legt Artikel 91(2) van Richtlijn 2006/112/EG van de Raad de regel vast als "de laatste verkoopkoers die, op het tijdstip waarop de btw verschuldigd wordt, op de meest representatieve wisselmarkt of wisselmarkten van de betrokken lidstaat wordt genoteerd, of een koers die onder verwijzing naar die markt of markten wordt bepaald."

Dat zou lastig te implementeren zijn over 27 lidstaten, dus voegt de richtlijn een praktische uitweg toe: lidstaten "aanvaarden in plaats daarvan het gebruik van de laatste wisselkoers die door de Europese Centrale Bank is gepubliceerd op het tijdstip waarop de belasting verschuldigd wordt." Omrekening tussen twee niet-euro-valuta's gebeurt "door gebruik te maken van de eurowisselkoers van elk van die valuta's" — met andere woorden: kruis via de EUR in plaats van het paar rechtstreeks te noteren. Lidstaten kunnen een melding verlangen dat je van deze optie gebruikmaakt.

Voor invoer verwijst Artikel 91(1) juist naar de douaneregels voor de vaststelling van de douanewaarde — een werkelijk andere koers, op een werkelijk andere datum, in hetzelfde grootboek. Behandelt je systeem "EU-btw" als één omrekenregel, dan is het al fout.

De basis onder dit alles: IAS 21

Fiscale regels rusten op je verslaggevingsgrondslag, en voor IFRS-rapporteurs is die grondslag IAS 21. Vier bepalingen doen het meeste werk:

  • Een transactie in vreemde valuta wordt eerst opgenomen tegen de spotkoers op de transactiedatum (IAS 21.21).
  • Een gemiddelde koers is als vereenvoudiging toegestaan, maar alleen "zolang de wisselkoersen niet aanzienlijk fluctueren" (IAS 21.22). Dat is een voorwaarde, geen standaardkeuze — en het is de bepaling die in een volatiel kwartaal geruisloos sneuvelt.
  • Monetaire posten worden op de verslagdatum omgerekend tegen de slotkoers (IAS 21.23).
  • Niet-monetaire posten die tegen historische kostprijs worden gewaardeerd blijven op de koers van de transactiedatum staan en worden niet omgerekend.

US GAAP komt onder ASC 830 tot grotendeels vergelijkbare uitkomsten. Het praktische gevolg voor een developer is dat één transactie gedurende haar levensduur legitiem twee of drie verschillende koersen kan vergen — één bij opname, één bij periodeafsluiting, één bij afwikkeling — en je schema moet voor alle drie ruimte hebben. Onze gids over valutarisicobeheer voor bedrijven behandelt wat de resulterende winsten en verliezen commercieel betekenen.

Vijf omrekenfouten die uitmonden in herzieningen

1. Koersrichting

base=USD&symbols=EUR geeft euro's per dollar. base=EUR&symbols=USD geeft dollars per euro. De IRS-jaartabel is vreemde valuta per USD, dus je deelt; een Finexly-respons met base=EUR is USD per EUR, dus je vermenigvuldigt. Beide zijn correct; ze door elkaar halen niet.

De oplossing is saai en effectief: noem een kolom nooit rate. Noem hem quote_per_base en maak de richting ondubbelzinnig in het schema in plaats van in een commentaarregel.

2. Opnieuw bevragen in plaats van afspelen

Een controle in 2029 vraagt naar een transactie uit 2026. Roept je rapportagecode een live endpoint aan op het moment van rapportgeneratie, dan leveren twee runs van hetzelfde rapport twee verschillende getallen op. De koers die op een transactie is toegepast, is een feit over die transactie, geen opzoekactie — leg hem vast op het moment van omrekening. Historische endpoints bestaan om te backfillen en te reconciliëren, niet als vervanging van opslag; zie onze gids voor de API voor historische wisselkoersen voor de backfill-patronen.

3. Gemiddelde waar spot vereist is

Maandgemiddelden zijn handig en vaak toegestaan, maar IAS 21.22 verbindt er een voorwaarde aan, en eenmalige transacties vergen onder IRS-richtlijnen doorgaans de koers van de transactiedatum. Sla de methode naast de koers op, zodat je "waarom dit getal?" kunt beantwoorden zonder archeologie.

4. Ontbrekende dagen

Weekenden, nationale feestdagen en niet-TARGET-dagen hebben geen gepubliceerde koers. Elk systeem heeft een expliciete regel nodig — meestal "laatst gepubliceerde koers op of vóór de datum" — en het moet vastleggen welke regel is afgegaan. Een stille fallback is zes maanden later niet van een bug te onderscheiden. Dit overlapt rechtstreeks met caching- en foutafhandelingspraktijk.

5. Precisie en afronding

Sla meer decimalen op dan je toont, en rond precies één keer af, bij de laatste stap van presentatie of boeking. Bij elke tussenstap afronden levert over een paar duizend facturen een aansluitverschil op dat vervelend is om uit te leggen en onmogelijk terug te draaien. De rekenkunde behandelden we uitvoerig in valuta-afronding en decimalen.

Ontwerp de rate snapshot, niet de rate lookup

Het hele probleem klapt in elkaar zodra je omrekening niet langer als een functieaanroep ziet maar als een onveranderlijk record. Hier is een minimaal schema dat aan alle hierboven besproken eisen voldoet:

CREATE TABLE fx_rate_snapshot (
    id                BIGSERIAL PRIMARY KEY,
    transaction_id    BIGINT      NOT NULL,
    -- direction is in the name, not in a comment
    base_currency     CHAR(3)     NOT NULL,   -- e.g. 'EUR'
    quote_currency    CHAR(3)     NOT NULL,   -- e.g. 'USD'
    quote_per_base    NUMERIC(20,10) NOT NULL,

    rate_date         DATE        NOT NULL,   -- the date the rate APPLIES to
    method            TEXT        NOT NULL,   -- 'spot' | 'monthly_period' | 'yearly_average'
    fallback_applied  TEXT,                   -- 'previous_business_day' | NULL

    source            TEXT        NOT NULL,   -- 'finexly' | 'hmrc_monthly' | 'irs_yearly'
    source_reference  TEXT,                   -- file name, request id, or table year
    retrieved_at      TIMESTAMPTZ NOT NULL,   -- when WE obtained it

    amount_base       NUMERIC(20,4) NOT NULL,
    amount_quote      NUMERIC(20,4) NOT NULL,

    created_at        TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- one authoritative conversion per transaction per purpose
CREATE UNIQUE INDEX ux_fx_snapshot_txn_method
    ON fx_rate_snapshot (transaction_id, method, rate_date);

Drie kolommen dragen het grootste deel van het controlegewicht. rate_date is de datum waarop de koers van toepassing is en staat bewust los van retrieved_at, het moment waarop je hem hebt opgehaald — een koers voor 13 maart die tijdens een backfill in september is opgehaald is volkomen legitiem, en het paar timestamps zegt dat eerlijk. fallback_applied maakt van je weekendregel geen onzichtbaar gedrag meer, maar vastgelegd bewijs.

Merk op dat rijen nooit worden bijgewerkt. Wordt een koers gecorrigeerd, voeg dan een nieuwe rij toe en vervang de oude. Een audittrail die je kunt bewerken is geen audittrail.

Een verdedigbare historische koers ophalen

Vraag voor een koers op de transactiedatum de specifieke datum op in plaats van de actuele:

curl "https://api.finexly.com/v1/historical?date=2026-03-13&base=EUR&symbols=USD" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "base": "EUR",
  "date": "2026-03-13",
  "rates": {
    "USD": 1.0842
  }
}

Met base=EUR is de waarde dollars per euro — een factuur van € 10.000 wordt dus door vermenigvuldiging $ 10.842,00. Hier is het snapshot-on-write-patroon in Python, inclusief de fallback voor ontbrekende dagen en de velden die het bovenstaande schema verwacht:

import requests
from datetime import date, timedelta
from decimal import Decimal, ROUND_HALF_UP

API = "https://api.finexly.com/v1/historical"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}


def fetch_rate(base: str, quote: str, on: date, max_lookback: int = 5):
    """Return (quote_per_base, rate_date, fallback) for a given date.

    Walks back to the most recent published rate if `on` is a weekend
    or a market holiday, and reports which day it actually landed on.
    """
    for offset in range(max_lookback + 1):
        d = on - timedelta(days=offset)
        r = requests.get(
            API,
            params={"date": d.isoformat(), "base": base, "symbols": quote},
            headers=HEADERS,
            timeout=10,
        )
        r.raise_for_status()
        rates = r.json().get("rates", {})
        if quote in rates:
            fallback = "previous_business_day" if offset else None
            return Decimal(str(rates[quote])), d, fallback

    raise LookupError(f"No {base}/{quote} rate within {max_lookback} days of {on}")


def convert_for_filing(amount_base, base, quote, transaction_date):
    """Convert once, and return everything an auditor will ask for."""
    rate, rate_date, fallback = fetch_rate(base, quote, transaction_date)
    amount = Decimal(str(amount_base))
    converted = (amount * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)

    return {
        "base_currency": base,
        "quote_currency": quote,
        "quote_per_base": rate,
        "rate_date": rate_date.isoformat(),
        "method": "spot",
        "fallback_applied": fallback,
        "source": "finexly",
        "amount_base": amount,
        "amount_quote": converted,
    }


snapshot = convert_for_filing(10000, "EUR", "USD", date(2026, 3, 13))
print(snapshot["amount_quote"], snapshot["rate_date"], snapshot["fallback_applied"])
# -> 10842.00 2026-03-13 None

De functie rekent om en documenteert in één adem. Wat je persistentielaag ook is: schrijf dat dictionary weg voordat iets verderop het getal te zien krijgt. Dezelfde discipline betaalt zich uit bij facturatie in meerdere valuta's, SaaS-facturatie en grensoverschrijdende salarisadministratie — die allemaal in dezelfde belastingaangifte belanden.

Afstemmen op de officieel gepubliceerde koers

Haal voor Britse btw via route 2 het maandbestand van HMRC één keer per maand op en bewaar het als bron van waarheid voor die periode:

import csv, io, requests
from datetime import date

def hmrc_monthly_rates(year: int, month: int) -> dict:
    """HMRC monthly rates for VAT/customs. Note: month is NOT zero-padded."""
    url = (
        "https://www.trade-tariff.service.gov.uk/exchange_rates/view/files/"
        f"monthly_csv_{year}-{month}.csv"
    )
    resp = requests.get(url, timeout=15)
    resp.raise_for_status()

    reader = csv.DictReader(io.StringIO(resp.text))
    # Don't hard-code the header text: find the ISO-4217 code column by shape.
    code_col = next(
        c for c in reader.fieldnames if "code" in c.strip().lower()
    )
    return {
        row[code_col].strip(): row
        for row in reader
        if row.get(code_col) and len(row[code_col].strip()) == 3
    }

Voer vervolgens een periodieke driftcontrole uit: vergelijk elke opgeslagen snapshot met de officiële koers voor die periode en markeer alles buiten een bewust gekozen tolerantie. Kleine afwijkingen tussen een marktkoers en een gepubliceerde administratieve koers zijn te verwachten en meestal acceptabel — maar je wilt de omvang van het verschil kennen voordat een inspecteur het voor je uitrekent. Bouw je dit in een grootboek in, dan behandelen onze aantekeningen over integratie met boekhoudsoftware en over waar wisselkoers-API's hun data vandaan halen de herkomstvragen die daarna komen.

Een checklist vóór de aangifte voor data in meerdere valuta's

  1. Bij elk omgerekend bedrag is een koers opgeslagen. Geen enkel rapport herberekent een historische omrekening tijdens runtime.
  2. De koersrichting is ondubbelzinnig in kolomnamen, niet in documentatie.
  3. rate_date en retrieved_at zijn gescheiden velden, en beide zijn gevuld.
  4. De regel voor ontbrekende dagen is expliciet en vastgelegd, geen impliciete retry.
  5. Eén methode per transactieklasse, consistent toegepast over de hele periode — de daadwerkelijke wettelijke eis in zowel de VS als het VK.
  6. Afronden gebeurt één keer, bij de laatste stap, met een gedocumenteerde modus.
  7. Snapshotrijen zijn append-only. Correcties vervangen; ze overschrijven nooit.

Werk die zeven punten af en de vraag van de accountant wordt niet langer eng. Het antwoord wordt een query.

Veelgestelde vragen

Welke wisselkoers eist de IRS voor belastingaangifte? Geen enkele specifiek. De IRS stelt onomwonden dat zij "geen officiële wisselkoers" hanteert en "in het algemeen elke gepubliceerde wisselkoers accepteert die consistent wordt gebruikt." De standaard is de spotkoers die geldt wanneer je de post ontvangt, betaalt of toerekent; voor gestaag aangroeiende inkomsten publiceert de IRS een jaargemiddeldetabel en draagt zij aangevers op het buitenlandse bedrag te delen door de vermelde koers.

Mag ik voor de btw een valuta-API gebruiken in plaats van de gepubliceerde koersen van HMRC? Ja, binnen grenzen. VAT Notice 700 §7.6 maakt de Britse marktverkoopkoers op het tijdstip van levering tot standaard, dus een marktgebaseerde API-koers past binnen die route mits je hem consistent toepast. HMRC's eigen periodekoers is een expliciet alternatief dat je zonder voorafgaande melding kunt kiezen — maar eenmaal gekozen kun je niet terug zonder schriftelijke instemming. Een koers of methode die buiten beide routes valt, vereist een schriftelijk verzoek, en termijnkoersen worden niet geaccepteerd.

Moet ik de wisselkoers opslaan, of kan ik hem later opnieuw opzoeken? Sla hem op. Een opgeslagen koers maakt een omrekening reproduceerbaar; opnieuw bevragen maakt er een verse berekening van die mogelijk niet overeenkomt met de aangifte die je al hebt ingediend. Het opslaan van de koers, de datum, de bron en het moment waarop je hem hebt opgehaald is wat een getal in bewijs verandert.

Welke koers moet ik gebruiken voor een transactiedatum in het weekend of op een feestdag? Voor een niet-handelsdag is er geen gepubliceerde koers, dus je hebt een vastgelegde regel nodig — meestal de laatst gepubliceerde koers op of vóór de transactiedatum. Belangrijker dan welke regel je kiest, is dat die gedocumenteerd is, uniform wordt toegepast en op elke betrokken rij wordt vastgelegd.

Hoeveel decimalen moet ik opslaan voor fiscale doeleinden? Sla de volledige precisie op die je leverancier teruggeeft — zes tot tien decimalen is een verstandige kolombreedte — en rond pas af bij het boeken of tonen. Vroeg en herhaald afronden is de gebruikelijke oorzaak van aansluitverschillen die niemand meer kan herleiden.

Voldoet de ECB-koers aan de EU-btw-eisen? Artikel 91(2) van de btw-richtlijn verplicht lidstaten de laatste ECB-koers te accepteren die is gepubliceerd op het tijdstip waarop de belasting verschuldigd wordt, waarbij omrekeningen tussen valuta's via de eurokoers van elke valuta lopen. Sommige lidstaten verlangen een melding dat je van deze optie gebruikmaakt, en voor invoer gelden in plaats daarvan de douanewaarderingsregels.

Maak je koersen auditklaar

Klaar om realtime en historische wisselkoersen in je project te integreren? Vraag je gratis Finexly API-sleutel aan — geen creditcard nodig. Begin met 1.000 gratis requests per maand en schaal op naarmate je groeit. Raadpleeg de API-documentatie voor de volledige referentie van het historische endpoint, probeer de valutaomrekenaar voor losse controles, vergelijk valuta-API's als je leveranciers evalueert, en bekijk de prijsplannen wanneer je rapportagevolume groeit.

Dit artikel is een technische gids voor developers die systemen met meerdere valuta's bouwen. Het is geen fiscaal advies — bevestig je omrekenbeleid met een gekwalificeerde adviseur in elke jurisdictie waar je aangifte doet.

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 →