You need Bank Negara Malaysia’s Overnight Policy Rate live in your code, with reproducible historicals and ready-to-use statistics. By the end of this article you’ll query the BNM_OPR symbol from the Interest Rates API, read latest and historical values, build time series and OHLC, compute spreads, and wire the result into a loan or dashboard workflow using Python, JavaScript, PHP, and cURL.
What BNM_OPR Represents and How It’s Expressed
BNM_OPR is the Bank Negara Malaysia Overnight Policy Rate — the central policy benchmark that guides short-term interest rates in Malaysia. In the Interest Rates API, BNM_OPR is delivered as a numeric rate value. All examples in this article use the exact symbols and fields defined by the API. Values shown in example responses are illustrative where noted, using the documented response structure.
Frequency: the BNM_OPR symbol is monthly in the catalogue. In practice, you will often see a constant rate reported across business days until a policy change. The API normalizes publication so that:
- Latest: returns the current effective policy rate.
- Historical: for monthly symbols, the last day with data in that month is used if you request a date within that month.
- Time zone: dates are ISO (Y-m-d). Treat them as effective dates, not timestamps.
Learn more about the platform at https://interestratesapi.com or explore model coverage here: Interest Rates API MCP.
Base URL, Authentication, and Request Conventions
All endpoints are GET and live under a single base URL:
Base URL: https://interestratesapi.com/api/v1/
- Authentication: append api_key as a query parameter to every request (e.g., ?api_key=YOUR_API_KEY). Do not send API keys in headers.
- HTTP method: GET for all endpoints.
- Symbols: always use the exact identifier BNM_OPR when requesting the Bank Negara Malaysia policy rate.
Quick links:
- Register
- MCP
- Register for Interest Rates API
- Interest Rates API MCP
- Get started with Interest Rates API
Discover Symbols with /symbols
Use the catalogue to confirm symbol availability and metadata. Filter by category or currency if needed.
cURL
curl "https://interestratesapi.com/api/v1/symbols?category=central_bank&api_key=YOUR_API_KEY"
JSON response (illustrative, documented fields only)
{
"success": true,
"count": 2,
"symbols": [
{
"symbol": "FED_FUNDS",
"name": "US Federal Funds Rate",
"category": "central_bank",
"country_code": "US",
"currency_code": "USD",
"frequency": "daily",
"description": "The interest rate at which depository institutions lend reserve balances to each other overnight"
}
]
}
Key fields: each entry includes the symbol, category, country and currency codes, a frequency tag (BNM_OPR is monthly in the catalogue), and a description. Use this endpoint to verify you are targeting BNM_OPR before coding business logic.
Python (requests)
import requests
resp = requests.get(
"https://interestratesapi.com/api/v1/symbols",
params=dict(category="central_bank", api_key="YOUR_API_KEY"),
timeout=30
)
catalog = resp.json()
print(catalog.get("count"), "symbols returned")
JavaScript (fetch)
const res = await fetch(
"https://interestratesapi.com/api/v1/symbols?category=central_bank&api_key=YOUR_API_KEY"
);
const catalog = await res.json();
console.log(catalog.count);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/symbols?category=central_bank&api_key=YOUR_API_KEY";
$resp = file_get_contents($url);
$data = json_decode($resp, true);
echo $data["count"];
?>
Get the Latest BNM_OPR with /latest
Retrieve the most recent effective OPR for production dashboards, risk checks, and loan repricing.
cURL
curl "https://interestratesapi.com/api/v1/latest?symbols=BNM_OPR&api_key=YOUR_API_KEY"
JSON response (illustrative, uses documented fields)
{
"success": true,
"date": "2026-09-29",
"base": "MIXED",
"rates": {
"BNM_OPR": 5.33
},
"dates": {
"BNM_OPR": "2026-09-29"
},
"currencies": {
"BNM_OPR": "USD"
}
}
How to read it:
- rates: map of symbol to numeric rate; interpret as an annualized rate percentage (per the dataset design).
- dates: map of symbol to effective date for the returned value.
- currencies: base currency for the series; treat this as metadata, not a conversion.
Python (requests)
import requests
r = requests.get(
"https://interestratesapi.com/api/v1/latest",
params=dict(symbols="BNM_OPR", api_key="YOUR_API_KEY"),
timeout=30
)
data = r.json()
opr = data["rates"]["BNM_OPR"]
as_of = data["dates"]["BNM_OPR"]
print(f"BNM_OPR {opr}% as of {as_of}")
JavaScript (fetch)
const response = await fetch(
"https://interestratesapi.com/api/v1/latest?symbols=BNM_OPR&api_key=YOUR_API_KEY"
);
const data = await response.json();
const opr = data.rates.BNM_OPR;
const asOf = data.dates.BNM_OPR;
console.log(`BNM_OPR ${opr}% as of ${asOf}`);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/latest?symbols=BNM_OPR&api_key=YOUR_API_KEY";
$json = file_get_contents($url);
$data = json_decode($json, true);
$opr = $data["rates"]["BNM_OPR"];
$asOf = $data["dates"]["BNM_OPR"];
echo "BNM_OPR {$opr}% as of {$asOf}";
?>
Historical BNM_OPR on a Specific Date with /historical
Use historical to evaluate the rate on an exact calendar date. For monthly symbols, the API returns the value for the last day with data in that month if you request a date within that month.
cURL
curl "https://interestratesapi.com/api/v1/historical?date=2025-06-15&symbols=BNM_OPR&api_key=YOUR_API_KEY"
JSON response (illustrative, documented fields only)
{
"success": true,
"date": "2025-06-15",
"base": "USD",
"rates": { "BNM_OPR": 5.33 },
"currencies": { "BNM_OPR": "USD" }
}
Tip: cache historical results for past months; these values do not change retroactively unless the provider issues a revision.
Python (requests)
import requests
params = dict(date="2025-06-15", symbols="BNM_OPR", api_key="YOUR_API_KEY")
resp = requests.get("https://interestratesapi.com/api/v1/historical", params=params, timeout=30)
hist = resp.json()
print(hist["rates"]["BNM_OPR"])
JavaScript (fetch)
const url = "https://interestratesapi.com/api/v1/historical?date=2025-06-15&symbols=BNM_OPR&api_key=YOUR_API_KEY";
const res = await fetch(url);
const hist = await res.json();
console.log(hist.rates.BNM_OPR);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/historical?date=2025-06-15&symbols=BNM_OPR&api_key=YOUR_API_KEY";
$resp = json_decode(file_get_contents($url), true);
echo $resp["rates"]["BNM_OPR"];
?>
BNM_OPR Time Series with /timeseries
Pull a continuous range to power charts, model features, or backtests. Required parameters: start, end, symbols.
cURL
curl "https://interestratesapi.com/api/v1/timeseries?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY"
JSON response (illustrative, fields as documented)
{
"success": true,
"base": "USD",
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"rates": {
"BNM_OPR": {
"2025-01-02": 5.33,
"2025-01-03": 5.33,
"2025-01-06": 5.33
}
},
"frequencies": { "BNM_OPR": "daily" },
"currencies": { "BNM_OPR": "USD" }
}
Notes:
- rates: nested map date → value for the symbol.
- frequencies: frequency label for each symbol.
- Use business-day iteration when aggregating; central bank series often hold steady across weekdays until a decision date.
Python (requests)
import requests
r = requests.get(
"https://interestratesapi.com/api/v1/timeseries",
params=dict(start="2025-09-29", end="2026-09-29", symbols="BNM_OPR", api_key="YOUR_API_KEY"),
timeout=30
)
ts = r.json()
series = ts["rates"]["BNM_OPR"]
ordered = sorted(series.items()) # list of (date, value)
print(ordered[:3])
JavaScript (fetch)
const tsRes = await fetch(
"https://interestratesapi.com/api/v1/timeseries?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY"
);
const ts = await tsRes.json();
const entries = Object.entries(ts.rates.BNM_OPR).sort(([d1],[d2]) => d1.localeCompare(d2));
console.log(entries.slice(0, 3));
PHP
<?php
$url = "https://interestratesapi.com/api/v1/timeseries?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY";
$data = json_decode(file_get_contents($url), true);
$series = $data["rates"]["BNM_OPR"];
ksort($series);
print_r(array_slice($series, 0, 3, true));
?>
Fluctuation Stats with /fluctuation
Summarize the change, percent change, and high/low over a window. This is ideal for quick dashboards and risk triggers.
cURL
curl "https://interestratesapi.com/api/v1/fluctuation?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY"
JSON response (illustrative, per docs)
{
"success": true,
"rates": {
"BNM_OPR": {
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"start_value": 5.50,
"end_value": 5.33,
"change": -0.17,
"change_pct": -3.09,
"high": 5.50,
"low": 5.25
}
}
}
How to use:
- change and change_pct let you render green/red deltas without recomputing from the series yourself.
- high/low are the observed extremes over the requested range.
Python (requests)
import requests
resp = requests.get(
"https://interestratesapi.com/api/v1/fluctuation",
params=dict(start="2025-09-29", end="2026-09-29", symbols="BNM_OPR", api_key="YOUR_API_KEY"),
timeout=30
)
fx = resp.json()["rates"]["BNM_OPR"]
print(fx["change"], fx["change_pct"], fx["high"], fx["low"])
JavaScript (fetch)
const fxUrl = "https://interestratesapi.com/api/v1/fluctuation?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY";
const fxRes = await fetch(fxUrl);
const fx = await fxRes.json();
console.log(fx.rates.BNM_OPR.change, fx.rates.BNM_OPR.change_pct);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/fluctuation?start=2025-09-29&end=2026-09-29&symbols=BNM_OPR&api_key=YOUR_API_KEY";
$data = json_decode(file_get_contents($url), true);
$stats = $data["rates"]["BNM_OPR"];
echo "{$stats['change']} ({$stats['change_pct']}%)";
?>
BNM_OPR OHLC with /ohlc
Generate candlestick-style open, high, low, close for charting. OHLC is computed on-the-fly from daily data.
cURL
curl "https://interestratesapi.com/api/v1/ohlc?symbols=BNM_OPR&period=monthly&start=2025-09-29&end=2026-09-29&api_key=YOUR_API_KEY"
JSON response (illustrative)
{
"success": true,
"period": "monthly",
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"rates": {
"BNM_OPR": [
{
"period": "2025-01",
"open": 5.50,
"high": 5.50,
"low": 5.33,
"close": 5.33,
"data_points": 23
}
]
}
}
Notes:
- period: aggregation window (monthly by default).
- data_points: number of daily observations used for that period’s OHLC.
Python (requests)
import requests
params = dict(symbols="BNM_OPR", period="monthly", start="2025-09-29", end="2026-09-29", api_key="YOUR_API_KEY")
ohlc = requests.get("https://interestratesapi.com/api/v1/ohlc", params=params, timeout=30).json()
print(ohlc["rates"]["BNM_OPR"][0]["open"], ohlc["rates"]["BNM_OPR"][0]["close"])
JavaScript (fetch)
const ohlcRes = await fetch(
"https://interestratesapi.com/api/v1/ohlc?symbols=BNM_OPR&period=monthly&start=2025-09-29&end=2026-09-29&api_key=YOUR_API_KEY"
);
const ohlc = await ohlcRes.json();
console.log(ohlc.rates.BNM_OPR[0]);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/ohlc?symbols=BNM_OPR&period=monthly&start=2025-09-29&end=2026-09-29&api_key=YOUR_API_KEY";
$resp = json_decode(file_get_contents($url), true);
$first = $resp["rates"]["BNM_OPR"][0];
echo "Open {$first['open']} Close {$first['close']}";
?>
Compare Loan Interest Costs with /convert
Convert compares the total interest cost of a simple loan at the latest values of two symbols. It also returns the rate spread. This is useful for benchmarking OPR-linked loans versus another policy rate or a reference rate.
cURL
curl "https://interestratesapi.com/api/v1/convert?from=BNM_OPR&to=ECB_MRO&amount=100000&term_months=12&api_key=YOUR_API_KEY"
JSON response (illustrative, as documented)
{
"success": true,
"amount": 100000,
"term_months": 12,
"from": {
"symbol": "BNM_OPR",
"rate": 5.33,
"date": "2026-09-29",
"total_interest": 5330.00,
"total_payment": 105330.00
},
"to": {
"symbol": "ECB_MRO",
"rate": 4.50,
"date": "2026-09-29",
"total_interest": 4500.00,
"total_payment": 104500.00
},
"difference": {
"rate_spread": 0.83,
"interest_saved": 830.00
}
}
How to compute a spread or monthly payment in your app:
- Spread: spread = latest_BNM_OPR − comparator_rate (e.g., from convert.difference.rate_spread or by subtracting yourself from /latest).
- Monthly payment (level-payment loan, using your own amortization): PMT = A × [r_m / (1 − (1 + r_m)^−N)], where A is principal, r_m is monthly rate (latest_BNM_OPR / 100 / 12), and N is term in months. Use the returned numeric rate from /latest to compute r_m; do not hard-code values.
Python (requests)
import requests
params = dict(from="BNM_OPR", to="ECB_MRO", amount=100000, term_months=12, api_key="YOUR_API_KEY")
res = requests.get("https://interestratesapi.com/api/v1/convert", params=params, timeout=30).json()
spread = res["difference"]["rate_spread"]
print("Spread vs ECB_MRO:", spread, "%")
JavaScript (fetch)
const convRes = await fetch(
"https://interestratesapi.com/api/v1/convert?from=BNM_OPR&to=ECB_MRO&amount=100000&term_months=12&api_key=YOUR_API_KEY"
);
const conv = await convRes.json();
console.log("Interest saved:", conv.difference.interest_saved);
PHP
<?php
$url = "https://interestratesapi.com/api/v1/convert?from=BNM_OPR&to=ECB_MRO&amount=100000&term_months=12&api_key=YOUR_API_KEY";
$data = json_decode(file_get_contents($url), true);
echo $data["difference"]["rate_spread"];
?>
Production Considerations: Publication, Business Days, and Caching
Publication frequency: BNM_OPR is monthly in the symbol catalogue. However, the API provides daily continuity so that time series and OHLC can operate with business-day calendars. Expect the rate to remain constant between policy changes, with the effective date captured in dates maps.
Business days: When joining to market data that observes trading holidays, rely on your own calendar. The API returns ISO dates; it does not encode holiday schedules.
Caching: For production systems:
- Cache /latest for several minutes; refresh near local policy decision windows if your use case is time-sensitive.
- Cache /historical and /timeseries results aggressively for past months since revisions are uncommon.
- Respect rate limiting headers when present: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Error handling: handle JSON with shape success=false and an error string. Common statuses include 401 (missing/invalid api_key), 403, 404 (no data for date/range), 422 (validation), and 429 (quota exhausted). On 429, use Retry-After and the X-RateLimit-* headers to back off.
Field Interpretation Example (Real API Response)
The following is a real example for a different symbol (SOFR) to illustrate response shape and field semantics exactly as returned by the service:
{"success":true,"date":"2026-09-25","base":"USD","rates":{"SOFR":3.9},"dates":{"SOFR":"2026-09-25"},"currencies":{"SOFR":"USD"},"base_filter_note":null}
How to read:
- rates: map of symbol → numeric rate (percentage units).
- dates: per-symbol effective date for the returned value.
- currencies: base currency metadata.
- date at top-level is a convenience timestamp for the response set.
Apply the same field logic to BNM_OPR in your integration.
End-to-End Python: Fetch BNM_OPR, Compute a Payment, and Log a Spread
This short example shows how to fetch the latest BNM_OPR, compute a simple level-payment estimate from the returned value (no hard-coded rate), and compute a spread relative to another benchmark retrieved from /latest.
Python (requests)
import math
import requests
API = "https://interestratesapi.com/api/v1"
KEY = "YOUR_API_KEY"
# 1) Latest BNM_OPR and a comparator (ECB_MRO used here for demonstration)
latest = requests.get(
f"{API}/latest",
params=dict(symbols="BNM_OPR,ECB_MRO", api_key=KEY),
timeout=30
).json()
opr = latest["rates"]["BNM_OPR"] # percentage
ecb = latest["rates"]["ECB_MRO"] # percentage
as_of = latest["dates"]["BNM_OPR"]
# 2) Compute a loan monthly payment using the BNM_OPR value
principal = 250000.0
months = 240 # 20 years
r_m = (opr / 100.0) / 12.0
pmt = principal * (r_m / (1.0 - math.pow(1.0 + r_m, -months))) if r_m != 0 else principal / months
# 3) Spread vs comparator
spread = opr - ecb
print(f"As of {as_of}: BNM_OPR={opr}%, spread vs ECB_MRO={spread}%")
print(f"Estimated monthly payment at OPR: {pmt:.2f}")
This script avoids hard-coding a rate by using the value returned in latest.rates.BNM_OPR. Swap the comparator to any other available symbol if needed (see /symbols).
Real-World Use Cases for BNM_OPR
- Interest rate dashboards: show the current OPR, monthly OHLC, and weekly fluctuation deltas.
- Loan repricing: compute amortized payments from BNM_OPR plus a margin and compare against alternative benchmarks using /convert.
- Macro research: retrieve multi-year /timeseries data to analyze policy cycles and their transmission to lending rates.
- Risk models: feature-engineer change, high/low, and rolling volatility from /fluctuation and /ohlc.
- Fintech apps: display effective dates and cached latest values, syncing quietly on business days.
Build, test, and ship faster with the official API: interestratesapi.com. To start querying BNM_OPR today, use this call to action: Register. For catalogue and model coverage, visit the MCP.
FAQ
How often is BNM_OPR updated?
BNM_OPR is monthly in the catalogue. The API provides daily continuity for integration convenience; expect the value to remain steady until policy changes, with the effective date in the dates map.
What does the dates map represent?
It’s the effective date for the returned value of each symbol, per the API’s normalization. Use it to annotate charts and to align rate changes with your business-day calendar.
Can I query multiple symbols together?
Yes. Pass a comma-separated list via symbols in endpoints like /latest and /timeseries (e.g., symbols=BNM_OPR,ECB_MRO). Always include ?api_key=YOUR_API_KEY.
How do I handle missing data or 404s?
A 404 means no symbols matched or no data for the requested date/range. The error may include a details field indicating available ranges. Adjust your date window or symbol list accordingly.
How should I cache responses?
Cache /historical and /timeseries aggressively for past months. Cache /latest for several minutes and refresh around expected decision windows. Respect Retry-After and the X-RateLimit-* headers if you hit 429.
Ready to implement BNM_OPR in your system? Get started with Interest Rates API or Register for Interest Rates API and explore coverage in the Interest Rates API MCP. Build your integration at interestratesapi.com and ship reliable rate-driven features fast.




