Official Central Bank Exchange Rates in Xero

Xero sets organisation exchange rates from XE and does not accept a replacement feed. Here is how to book invoices, bills and bank transactions at the official central-bank rate through the Xero Accounting API.

Which endpoint: every guide below reads the bank's latest published table from GET /api/v1/central-bank/{bank}/latest (free key, one call per fetch) or the keyless evaluation copy at /api/public/central-bank/{bank}?format=json. Add ?source=USD&target=EUR for one resolved pair, format=xml or format=csv for importers that do not read JSON, and api_key= on the URL when the importer cannot send headers. Bank codes are on the central bank coverage map.

What Xero allows

Xero fills its organisation-level rates from XE.com once multicurrency is enabled and exposes no endpoint to replace that feed. What it does allow, in the UI and in the Accounting API, is a rate on each transaction: invoices, bills, credit notes, bank transactions and manual journals all carry a CurrencyRate. A compliant workflow therefore sets that field at creation time from the official table for the document date, rather than trying to change Xero's defaults.

The rate Xero expects

CurrencyRate is the number of document currency units per 1 unit of the organisation base currency. For a GBP organisation raising a EUR invoice you need EUR per GBP. Ask the API for exactly that pair and let it handle inversion or cross rates:

GET https://allratestoday.com/api/v1/central-bank/boe/latest?source=GBP&target=EUR
Authorization: Bearer art_live_your_key

{
  "bank": "boe",
  "rate_date": "2026-09-16",
  "source": "GBP",
  "target": "EUR",
  "rate": 1.1662,
  "derived": false,
  "method": "published"
}

Use the dated endpoint /api/v1/central-bank/{bank}/{YYYY-MM-DD} when the invoice date is not today; if the bank did not publish that day you get the prior table and published_on_requested_date: false, which is the rate in force and the one to apply.

Create the invoice with the official rate

Python, using the xero-python SDK after the usual OAuth 2.0 connection:

import requests
from xero_python.accounting import AccountingApi, Invoice, LineItem

BANK = "boe"                      # the publisher your rule names
BASE = "GBP"                      # Xero organisation base currency

def official_rate(target, on_date):
    url = f"https://allratestoday.com/api/v1/central-bank/{BANK}/{on_date}"
    r = requests.get(url, params={"source": BASE, "target": target},
                     headers={"Authorization": "Bearer art_live_your_key"}, timeout=10)
    r.raise_for_status()
    body = r.json()
    return body["rate"], body["rate_date"], body["derived"]

rate, rate_date, derived = official_rate("EUR", "2026-09-16")

invoice = Invoice(
    type="ACCREC",
    contact={"ContactID": contact_id},
    date="2026-09-16",
    due_date="2026-10-16",
    currency_code="EUR",
    currency_rate=rate,                          # EUR per 1 GBP, from the Bank of England table
    reference=f"BoE {rate_date}{' (derived)' if derived else ''}",
    line_items=[LineItem(description="Consulting", quantity=1, unit_amount=1000, account_code="200")],
)
AccountingApi(api_client).create_invoices(xero_tenant_id, invoices={"Invoices": [invoice]})

Writing the bank and rate_date into the reference, or a tracking category, keeps the source of the rate on the document for the auditor. The same field exists on BankTransactions, CreditNotes and ManualJournals.

Bulk month-end revaluation

Xero's unrealised gains report uses its own closing rate. If your statutory close must use the central bank's month-end table, pull that table once with /api/v1/central-bank/{bank}/{last-business-day}, and post the revaluation as a manual journal at those rates. The Excel download gives the same table as a dated file if the close is done in a spreadsheet.

Notes

Other systems: Business Central · Xero · QuickBooks Online · SAP · Odoo · all integrations. Endpoint reference: Central Bank Rates.