Policy-rate history arrives oldest first, so you can detect changes by comparing consecutive observations. Compare rate for a point rate, or both bounds for a range.
Do it with the API
- Set your API key Set the
NARWHAL_API_KEYenvironment variable in the environment that will run Python. Install therequestslibrary for that interpreter. After public access starts, policy-rate requests are available live on every plan, including Free. Each request consumes one metered unit, including each page of history. - Read the current rate Request
/v1/economics/USA/central-bank/policy-rateand/v1/economics/CAN/central-bank/policy-rate. Country codes use uppercase ISO3. The USA response hasrate_type: "range",lower_boundandupper_bound. Canada hasrate_type: "point",rateandqualifier. Values are decimal strings in percent per annum. The complete script prints both responses so you can inspect their shapes. - Read and paginate history Request
/v1/economics/{country}/central-bank/policy-rate/historywithstart=2024-01-01. Read observations in their returned order, then passnext_cursorascursoruntil it is null. Keep the previous value across page boundaries. The script usesparams=to encode query values. A cursor pins publications for 24 hours; an expired cursor returns 409. - Compare consecutive values For each point observation, compare
ratewith the previous point value. For a range, compare the pairlower_boundandupper_bound. Convert strings to decimals before comparison. The first observation establishes the baseline, so the function cannot report whether that observation changed from a value before the requested history window. - Choose the returned date Print
effective_onwhen it is present; otherwise printobserved_on. Each response has exactly one of those fields. An optionalannounced_ondescribes the announcement date. Keep the returned instrument and rate shape when you store observations, since a history can contain point rates and ranges. The comparison function preserves that distinction in its values. - Run the script daily Save the complete example as
track_rates.pyin your home directory. Run it once with your key set, then schedule it daily with0 0 * * * /usr/bin/env python3 "$HOME/track_rates.py". Configure the scheduled environment with your API key and a PATH that finds the interpreter withrequestsinstalled. The script prints only changed observations after the two current-response previews.import json import os from decimal import Decimal import requests BASE_URL = "https://api.narwhalapi.com" START = "2024-01-01" HEADERS = { "Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}" } def get_json(path, params=None): response = requests.get( BASE_URL + path, headers=HEADERS, params=params, timeout=10, ) response.raise_for_status() return response.json() def rate_value(observation): if observation["rate_type"] == "point": return Decimal(observation["rate"]) if observation["rate_type"] == "range": return ( Decimal(observation["lower_bound"]), Decimal(observation["upper_bound"]), ) raise ValueError("Unexpected rate_type") def changes(country): path = f"/v1/economics/{country}/central-bank/policy-rate/history" params = {"start": START} previous = None result = [] while True: page = get_json(path, params=params) for observation in page["observations"]: current = rate_value(observation) if previous is not None and current != previous: date = ( observation["effective_on"] if "effective_on" in observation else observation["observed_on"] ) result.append((date, previous, current)) previous = current cursor = page["next_cursor"] if cursor is None: return result params = {"start": START, "cursor": cursor} if __name__ == "__main__": for country in ("USA", "CAN"): current = get_json( f"/v1/economics/{country}/central-bank/policy-rate" ) print(json.dumps(current, indent=2)) for date, old, new in changes(country): print(country, date, old, new)
The function changes(country) returns a list of tuples containing the date, old value and new value. A point value is a decimal; a range is a pair of decimals in lower-bound, upper-bound order. An empty list means no value changes were found between the returned observations.
The saved USA example has a range of 3.50 to 3.75 percent, effective 11 December 2025. The function represents that range as (Decimal("3.50"), Decimal("3.75")). It includes the observation in its results only if the preceding returned value differs. The saved example supplies no previous value, so it cannot establish the size or direction of a change.
What the date means by bank
| Bank | History rows | How to read the date |
|---|---|---|
| Federal Reserve | Rate-change dates from 25 June 2003 | effective_on is the date the rate took effect. |
| Reserve Bank of Australia | Change-date rows from 23 January 1990 | effective_on is the date the rate took effect. |
| Czech National Bank | Change-date rows from 8 December 1995 | effective_on is the date the rate took effect. |
| Bank of Canada | Observations from 21 April 2009 | observed_on is when the source recorded the value. |
| Bank Negara Malaysia | Observations from 2004 | observed_on is when the source recorded the value. |
| Sveriges Riksbank | Daily rows from 1 June 1994 | observed_on is the source observation date. |
| Norges Bank | Business-day rows from 1 January 1991 | Use the returned date field for each observation. |
| Banco Central do Brasil | Calendar-day rows from 5 March 1999 | Dates not yet in force are not served. |
Sweden, Norway and Brazil can repeat the same rate across successive rows. Count a change only when the value differs. The USA, Australia and Czechia use change-date rows. For Canada and Malaysia, the observation date can be later than the decision, so an observed change does not establish the decision date.
How often should I check?
The rate only changes at decisions, so a daily check is enough. Each current-rate check is one call per country, plus one call per history page you request. The example reads current rates and history for both countries; those are separate requests.
If you store previous results, compare new observations with the last stored value. Retain that baseline when you narrow the history window, or the first returned observation will have no preceding value to compare.
Does a missing date mean the rate did not change?
A missing date means there is no returned source observation for that date. Narwhal does not invent missing days. A request with date=YYYY-MM-DD returns the latest source observation at or before the requested date; without date, it uses today in UTC.
Future effective dates never replace the current rate early. Preserve the date returned with the value when you record a change. Use policy-rate documentation for the response contract and API errors when a request fails.
Published .
Next steps
- API referencePolicy-rate API documentation
- Central-bank interest-rate coverage
- How-toCompare policy rates across countries
- How-toList central-bank meetings
Create a free key and call the API from your own code. Get a free API key
