The API returns the official exchange rates published by Banca Națională a României (BNR), in lei (RON), for the latest day or for any past date, in JSON. It is free and public; no key is needed.
Version 1 replaces /api/json.php, which keeps working unchanged for existing users. See Moving from json.php for the differences.
https://www.cursbnr.ro/api/v1
All endpoints use the GET method. Responses are JSON (UTF-8) unless you ask for plain text with format=text. Cross-origin requests from browsers are allowed (Access-Control-Allow-Origin: *).
| Endpoint | Returns |
|---|---|
/api/v1/rates | The latest rates, or the rates for date if given |
/api/v1/rates/latest | The latest rates |
/api/v1/rates/YYYY-MM-DD | The rates for that date (same as /rates?date=YYYY-MM-DD) |
/api/v1/currencies | The supported currency codes |
| Parameter | Default | Description |
|---|---|---|
currency | EUR |
One currency code or a comma-separated list, e.g. currency=EUR,USD,GBP. Codes are case-insensitive. See Supported currencies. |
all | off | Returns every currency BNR published a rate for that day, instead of the ones in currency: ?all (also all=1 or all=true). Currencies with no rate that day are left out. It cannot be combined with currency or with format=text. |
date | today | The date in the format YYYY-MM-DD. BNR does not publish rates on weekends and public holidays; for those days the most recent earlier rates are returned, and the response shows both dates (see Response). |
format | json |
json or text. With text the response is only the value, as plain text; it needs exactly one currency. |
The response has the same shape for one currency or several:
{
"date": "2026-10-16",
"requested_date": "2026-10-18",
"quote": "RON",
"rates": {
"EUR": 5.3368,
"USD": 4.743
}
}
| Field | Description |
|---|---|
date | The date the rates were published by BNR. |
requested_date | Only present when it differs from date, i.e. when no rates were published on the date you asked for and earlier ones were returned. |
quote | The currency the values are expressed in. Always RON: BNR publishes rates only against the leu, and the API does not calculate cross rates. |
rates | One entry per requested currency. Each value is a number: how many lei one unit of that currency is worth, or null if BNR published no rate for it that day (for example SKK, which was replaced by the euro in 2009). With all, such currencies are left out instead. Values are numbers, so trailing zeros are not shown (4.7430 is returned as 4.743). |
Currencies quoted per 100 units. BNR quotes HUF, JPY, KRW, IDR and ISK per 100 units. You can ask for either form:
100HUF (also 100JPY, 100KRW, 100IDR, 100ISK) returns the BNR rate for 100 units: "100HUF": 1.4695 means 100 forints are worth 1.4695 lei.HUF (also JPY, KRW, IDR, ISK) returns the rate for 1 unit, i.e. the BNR rate divided by 100: "HUF": 0.014695.The key in rates is the code you asked for, so currency=HUF,100HUF returns both.
EUR, USD, XAU (gold, per gram), AUD, CAD, CHF, CZK, DKK, EGP, GBP, 100HUF, 100JPY, MDL, NOK, PLN, SEK, TRY, XDR, RUB, BGN, SKK, ZAR, BRL, CNY, INR, 100KRW, MXN, NZD, RSD, UAH, AED, THB, HKD, 100IDR, ILS, 100ISK, MYR, PHP, SGD, and the per-unit codes HUF, JPY, KRW, IDR and ISK.
The same list is returned by /api/v1/currencies, with the number of units each code is quoted per:
{
"quote": "RON",
"currencies": [
{ "code": "EUR", "units": 1 },
{ "code": "100HUF", "units": 100 },
{ "code": "HUF", "units": 1 },
...
]
}
GET https://www.cursbnr.ro/api/v1/rates
{"date": "2026-10-16", "quote": "RON", "rates": {"EUR": 5.3368}}
GET https://www.cursbnr.ro/api/v1/rates?currency=EUR,USD,100HUF
{"date": "2026-10-16", "quote": "RON", "rates": {"EUR": 5.3368, "USD": 4.743, "100HUF": 1.4695}}
GET https://www.cursbnr.ro/api/v1/rates?all
{"date": "2026-10-16", "quote": "RON", "rates": {"EUR": 5.3368, "USD": 4.743, …}}
Works with dates too: /api/v1/rates/2026-10-16?all.
GET https://www.cursbnr.ro/api/v1/rates/2026-10-18?currency=USD
{"date": "2026-10-16", "requested_date": "2026-10-18", "quote": "RON", "rates": {"USD": 4.743}}
GET https://www.cursbnr.ro/api/v1/rates?currency=HUF,100HUF
{"date": "2026-10-16", "quote": "RON", "rates": {"HUF": 0.014695, "100HUF": 1.4695}}
GET https://www.cursbnr.ro/api/v1/rates?currency=USD&format=text
4.743
$json = file_get_contents('https://www.cursbnr.ro/api/v1/rates?currency=EUR,USD');
$data = json_decode($json, true);
echo $data['rates']['EUR'];
const res = await fetch('https://www.cursbnr.ro/api/v1/rates?currency=EUR,USD');
const data = await res.json();
console.log(data.rates.EUR);
Errors use standard HTTP status codes, with a JSON body that has a machine-readable code and a message:
{
"error": {
"code": "invalid_currency",
"message": "Unknown currency code: XYZ. See /api/v1/currencies for the supported codes."
}
}
| HTTP status | code | When |
|---|---|---|
| 400 | invalid_date | The date is not a valid YYYY-MM-DD date |
| 400 | invalid_currency | A currency code is not supported, none was given, or both currency and all were given |
| 400 | invalid_format | format is not json or text, or format=text was used with more than one currency or with all |
| 400 | invalid_parameter | all has a value other than empty, 1, true, 0 or false |
| 404 | no_rates | No rates exist on or before the requested date, or (with format=text) BNR published no rate for that currency |
| 404 | not_found | The endpoint does not exist |
| 405 | method_not_allowed | A method other than GET was used |
| 429 | rate_limited | The rate limit was exceeded (see below) |
| 503 | unavailable | The rates database is temporarily unavailable |
Each IP address can make 30 requests per minute. Every response includes these headers:
X-RateLimit-Limit: the number of requests allowed per minute (30)X-RateLimit-Remaining: how many are left in the current minuteX-RateLimit-Reset: when the current minute ends, as a Unix timestampAbove the limit the API answers with HTTP 429 and a Retry-After header giving the number of seconds to wait. BNR publishes new rates once per business day, around 13:00 Romanian time, so there is rarely a reason to ask for the latest rates more often than every few minutes.
Responses carry caching headers, so browsers and HTTP clients can reuse them:
ETag; send it back in If-None-Match and the API answers 304 Not Modified if nothing changed.| json.php | v1 |
|---|---|
/api/json.php?currency=USD&date=2024-12-31 | /api/v1/rates?currency=USD&date=2024-12-31 or /api/v1/rates/2024-12-31?currency=USD |
One currency: {"date", "currency", "value"}Several: {"date", "values"} | Always {"date", "quote", "rates"} |
Values as strings: "4.7430" | Values as numbers: 4.743 |
&number | &format=text (&number also still works) |
| Errors with HTTP status 200 | Errors with HTTP status 400, 404, 429 or 503 |
Last updated: October 21, 2026