Official Central Bank Exchange Rates in Odoo

Load central-bank and tax-authority exchange rates into Odoo res.currency.rate over XML-RPC, with the inversion Odoo expects and a scheduled action that runs after the bank publishes.

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 Odoo does out of the box

Odoo's Automatic Currency Rates setting (module currency_rate_live) can pull from a fixed list of providers: the ECB, the Swiss Federal Tax Administration, the Bank of Canada, Banxico and a handful of others. If your publisher is on that list, use it. If it is not, or you need one feed that covers every subsidiary's central bank, write the rows yourself. Rates live in res.currency.rate and the API for that model is stable across versions.

The rate Odoo expects

The stored rate field is units of the foreign currency per 1 unit of the company currency. For a EUR company and USD that is USD per EUR, which is the ECB's own direction. For an INR company on the Reserve Bank of India, which publishes INR per USD, you need the inverse. Ask the API for the pair in Odoo's direction and it will invert or cross for you, flagging the result with derived:

GET /api/v1/central-bank/rbi/latest?source=INR&target=USD
{ "bank": "rbi", "rate_date": "2026-09-16", "source": "INR", "target": "USD",
  "rate": 0.011325, "derived": true, "method": "inverse" }

Odoo 14 and later also expose inverse_company_rate (company currency per foreign unit) on the same model; writing either field is fine, but be consistent so a rerun updates rather than fights.

Load the rates over XML-RPC

import xmlrpc.client, requests

URL, DB, USER, PWD = "https://erp.example.com", "prod", "bot@example.com", "api-key"
BANK = "ecb"

common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common")
uid = common.authenticate(DB, USER, PWD, {})
models = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/object")
call = lambda model, method, *args, **kw: models.execute_kw(DB, uid, PWD, model, method, list(args), kw)

company = call("res.company", "read", [1], fields=["currency_id"])[0]
home = call("res.currency", "read", [company["currency_id"][0]], fields=["name"])[0]["name"]
active = {c["name"]: c["id"] for c in call("res.currency", "search_read", [["active", "=", True]], fields=["name"])}

table = requests.get(f"https://allratestoday.com/api/v1/central-bank/{BANK}/latest",
                     headers={"Authorization": "Bearer art_live_your_key"}, timeout=10).json()

for code, currency_id in active.items():
    if code == home:
        continue
    r = requests.get(f"https://allratestoday.com/api/v1/central-bank/{BANK}/latest",
                     params={"source": home, "target": code},
                     headers={"Authorization": "Bearer art_live_your_key"}, timeout=10)
    if r.status_code == 404:
        continue                                   # bank does not publish this currency
    body = r.json()
    vals = {"currency_id": currency_id, "name": body["rate_date"], "rate": body["rate"], "company_id": 1}
    existing = call("res.currency.rate", "search",
                    [["currency_id", "=", currency_id], ["name", "=", body["rate_date"]], ["company_id", "=", 1]])
    if existing:
        call("res.currency.rate", "write", existing, vals)
    else:
        call("res.currency.rate", "create", vals)
    print(code, body["rate_date"], body["rate"], body["method"])

The first request fetches the whole table so you can see what the bank covers; each pair request afterwards is one call and handles direction. Cache per date if you run it more than once a day.

Run it as a scheduled action

Put the loop in a small custom module and register an ir.cron that runs daily after the bank's publication time, or run the script from the server's scheduler and let it talk to Odoo over XML-RPC as above. Inside Odoo the same logic uses self.env['res.currency.rate'] and requests, with the API key in a system parameter rather than the code.

Notes

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