Follow central bank policy rates and the market-implied path

Read each central bank's current policy rate and its change history from the economics endpoints, then add the market-implied path from the predictions API. The rate says where policy stands today. The path gives the odds of a cut, hold or hike at each meeting ahead.

Read the current policy rate

GET /v1/economics/{country}/central-bank/policy-rate returns the rate now in force for a country. This request asks for the United States.

curl, the US policy rate
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/economics/USA/central-bank/policy-rate"
Response
{
  "country": "USA",
  "authority": "federal_reserve",
  "currency": "USD",
  "instrument": "federal_funds_target_range",
  "unit": "percent",
  "effective_on": "2026-09-17",
  "observed_on": null,
  "announced_on": null,
  "rate_type": "range",
  "lower_bound": "3.75",
  "upper_bound": "4.00"
}

The Federal Reserve sets a target range, so rate_type is range and the response has lower_bound 3.75 and upper_bound 4.00 in percent. The range took effect on effective_on 2026-09-17. A bank that sets a single rate has rate_type point and one rate field instead of the two bounds.

CountryinstrumentRateDate field
USAfederal_funds_target_range3.75 to 4.00effective_on 2026-09-17
GBRbank_rate3.75effective_on 2025-12-18
JPNovernight_call_rate_target1.25observed_on 2026-10-27

See how the rate got there

Add /history to read the series of rate observations. The start parameter sets the first date, and limit and cursor page through long histories. This request starts in September 2025.

curl, US policy rate history from a start date
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/economics/USA/central-bank/policy-rate/history?start=2025-09-01"
Response
{
  "country": "USA",
  "observations": [
    {
      "country": "USA",
      "authority": "federal_reserve",
      "currency": "USD",
      "instrument": "federal_funds_target_range",
      "unit": "percent",
      "effective_on": "2025-09-18",
      "observed_on": null,
      "announced_on": null,
      "rate_type": "range",
      "lower_bound": "4.00",
      "upper_bound": "4.25"
    },
    {
      "country": "USA",
      "authority": "federal_reserve",
      "currency": "USD",
      "instrument": "federal_funds_target_range",
      "unit": "percent",
      "effective_on": "2025-10-30",
      "observed_on": null,
      "announced_on": null,
      "rate_type": "range",
      "lower_bound": "3.75",
      "upper_bound": "4.00"
    }
  ],
  "next_cursor": null
}

The full response has four rows, oldest first, and the block shows two. Subtracting each upper bound from the one before gives the moves: -25 basis points on 2025-10-30, -25 on 2025-12-11 and +25 on 2026-09-17. The script below does that subtraction with Decimal so no floating point error enters a rate. For a long history, pass end as well, or follow next_cursor until it is null.

Read the market-implied path

GET /v1/predictions/policy-paths/federal-reserve returns the odds for each coming meeting. The path holds changes only and never the current rate, so read the rate from the endpoint above.

curl, the Federal Reserve policy path
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/predictions/policy-paths/federal-reserve"
Response
{
  "as_of": "2026-11-03T02:34:00Z",
  "meetings": [
    {
      "event_id": "usa-fed-2026-12",
      "p_cut": "0.0096",
      "p_hold": "0.2533",
      "p_hike": "0.7371",
      "expected_change_bps": "18.6",
      "cumulative_change_bps": "18.6",
      "resolves_at": "2026-12-09T19:00:00Z"
    },
    {
      "event_id": "usa-fed-2027-01",
      "p_cut": "0.0280",
      "p_hold": "0.7055",
      "p_hike": "0.2665",
      "expected_change_bps": "6.0",
      "cumulative_change_bps": "24.6",
      "resolves_at": "2027-01-27T19:00:00Z"
    }
  ]
}

as_of dates the odds. For each meeting, resolves_at is the decision time and p_cut, p_hold and p_hike are probabilities that sum to 1. The first meeting has a hike probability of 73.7 percent and an expected_change_bps of 18.6. cumulative_change_bps adds the meetings up: 18.6 plus 6.0 gives 24.6 after the second meeting.

Change the authority in the path for other banks, for example bank-of-japan. A response with an empty meetings list means no meetings are listed for that authority, which was the case for bank-of-england when this page was written.

Compare three banks in one table

A watch list needs the same columns for every bank. Take the rate from the rate endpoint and the first meeting from the path for the bank's authority name: federal-reserve, bank-of-england or bank-of-japan.

CountryAuthorityRate nowFirst meeting in the pathHike odds
USAfederal-reserve3.75 to 4.002026-12-0973.7 percent
GBRbank-of-england3.75None listedNot available
JPNbank-of-japan1.252026-12-1883.6 percent

The United Kingdom row stays empty because its path had no meetings when this page was written, while its rate is still current. Keep a row for such a bank in your table anyway. Showing the rate with a blank path is more useful than dropping the bank. Re-read each path before you rely on it, because as_of moves as the odds are updated.

Put the rate and the path in one script

  1. Get an API key Get an API key at signup and keep it in NARWHAL_API_KEY.
  2. Read the rate for each country The script prints the instrument, the rate and the date. It uses effective_on and falls back to observed_on when effective_on is null.
  3. Compute the moves and the odds Differences between consecutive upper bounds give the moves in basis points, and the first two meetings print their hike and hold odds as percentages.
    Python, rates, moves and odds
    import os
    from decimal import Decimal
    import requests
    
    BASE = "https://api.narwhalapi.com"
    HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}
    
    
    def get(path, **params):
        r = requests.get(f"{BASE}{path}", headers=HEADERS, params=params, timeout=10)
        r.raise_for_status()
        return r.json()
    
    
    def text(o):
        if o["rate_type"] == "range":
            return f"{o['lower_bound']} to {o['upper_bound']}"
        return o["rate"]
    
    
    def top(o):
        return Decimal(o["upper_bound"] if o["rate_type"] == "range" else o["rate"])
    
    
    for country in ("USA", "GBR", "JPN"):
        o = get(f"/v1/economics/{country}/central-bank/policy-rate")
        print(country, o["instrument"], text(o), "since", o["effective_on"] or o["observed_on"])
    
    rows = get("/v1/economics/USA/central-bank/policy-rate/history", start="2025-09-01")["observations"]
    for before, now in zip(rows, rows[1:]):
        print(now["effective_on"], int((top(now) - top(before)) * 100), "bp")
    
    for m in get("/v1/predictions/policy-paths/federal-reserve")["meetings"][:2]:
        print(m["resolves_at"][:10], "hike", f"{Decimal(m['p_hike']) * 100:.1f}%",
              "hold", f"{Decimal(m['p_hold']) * 100:.1f}%", "expected", m["expected_change_bps"], "bp")
Output of the script
USA federal_funds_target_range 3.75 to 4.00 since 2026-09-17
GBR bank_rate 3.75 since 2025-12-18
JPN overnight_call_rate_target 1.25 since 2026-10-27
2025-10-30 -25 bp
2025-12-11 -25 bp
2026-09-17 25 bp
2026-12-09 hike 73.7% hold 25.3% expected 18.6 bp
2027-01-27 hike 26.6% hold 70.6% expected 6.0 bp

Which date shows when a rate changed?

Use effective_on when it has a value, and observed_on otherwise. The United States and the United Kingdom returned effective_on. Japan returned observed_on with effective_on set to null. announced_on was null in all three responses.

Can I read the rate on a past date?

Yes. Pass date to the policy rate endpoint. A request with date=2025-11-15 for the United States returned the 3.75 to 4.00 range with effective_on 2025-10-30, the date that range began.

Does the path contain the current rate?

No. It carries odds and expected moves in basis points for each meeting, so add the move to the rate you read from the policy rate endpoint. Divide basis points by 100 to get percentage points: 18.6 basis points for the first meeting is 0.186 percentage points.

That is a probability-weighted average across cut, hold and hike outcomes, so it is a blend of outcomes and not the size of any move the bank will announce.

Published .

Next steps