You can use Python and Narwhal to pull holidays, release dates, economics, commodity spot prices, exchange rates and prediction-market odds with one API key. Use requests to read the JSON responses, then schedule the script with cron for a daily pull.
What you'll set up
Python 3 with requests gives you a script that calls the Narwhal REST API and reads the response fields for each data area. You will have a shared request helper, a sample for each area and a daily cron entry. Narwhal uses one account and one API key across its data APIs; REST and MCP share that key. The examples use https://api.narwhalapi.com as the base URL and send Authorization: Bearer YOUR_KEY. The paths select the data area, while each call reads the matching field from its JSON response.
Set your API key
- Create a free key Create your free key at narwhalapi.com/signup. The same key goes in the Authorization header for each request, so you can use it throughout the script.
- Store the key in your shell environment Set
NARWHAL_API_KEYin the shell where you will use curl and Python, replacing the placeholder with your key. The Python helper reads this environment variable when it builds the Bearer header.API keyexport NARWHAL_API_KEY=... - Install requests for Python 3 Install
requestsfor the Python script. There is also anarwhalapiSDK on PyPI, available withpip install narwhalapi.Install requestspip install requests
Pull data before writing Python
- Request the holiday JSON Use curl with the environment variable you set in your shell. This request sends the Bearer header to the holiday endpoint and prints the JSON before you write Python code.Holiday request
curl -H "Authorization: Bearer $NARWHAL_API_KEY" "https://api.narwhalapi.com/v1/calendars/holidays/USA/2026" - Read the holiday rows Look in
holidaysfor the returned rows. Each row has adateandname; the Python sample uses these fields to print the first holiday after it counts the rows.
Pull data with code
Use the shared get helper for all six requests. It combines the API base URL with a path, sends the header, passes query parameters and reads JSON after raise_for_status(). The calls then select the response field for each area, so the print statements can work with holiday rows, releases, observations, a spot price, exchange rates and meeting probabilities.
Query parameters stay with the individual call: the release-date request includes the country, date boundaries and limit, while the FX request includes currencies. The helper sends them through params, then returns the parsed response. Keep the helper above the area samples so each sample can use get, API and HEADERS. Each sample below reuses that helper with its own path and response field.
import os
import requests
API = "https://api.narwhalapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}
def get(path, **params):
r = requests.get(API + path, params=params, headers=HEADERS, timeout=10)
r.raise_for_status()
return r.json()
holidays = get("/v1/calendars/holidays/USA/2026")["holidays"]
releases = get("/v1/economics/calendar", countries="USA", start_date="2026-11-01", end_date="2026-11-30", limit=100)["releases"]
cpi = get("/v1/economics/USA/cpi/observations", start="2025-09")["observations"]
brent = get("/v1/commodities/spot/BRENT/USD")["price"]
fx = get("/v1/fx/rates/USD", currencies="EUR,IDR")["rates"]
fed = get("/v1/predictions/policy-paths/federal-reserve")["meetings"]
print(len(holidays), holidays[0]["date"], holidays[0]["name"])
print(len(releases), releases[0]["period"], releases[0].get("at"))
print(cpi[-1]["period"], cpi[-1]["value"])
print("Brent", brent)
print({r["currency"]: r["rate"] for r in fx})
print(fed[0]["resolves_at"][:10], fed[0]["p_hold"])Run it on a schedule
A cron entry can run the script once a day. Use the supplied entry with your script at /path/pull.py; it calls /usr/bin/python3 and appends the printed output to pull.log. The script makes six calls a day, a small number of requests.
0 7 * * * /usr/bin/python3 /path/pull.py >> pull.logSet NARWHAL_API_KEY in the environment the job runs in. On a shared machine, put the key in a file the job sources, keeping it out of the crontab. The scheduled script reads the same variable as the shell example, and its requests use the same Bearer header.
Holidays in Python
Read holidays to get the date and name of each holiday. The response from 24 October 2026 contains 11 US holidays for 2026, with 1 January, New Year's Day, first. This sample uses the shared helper and prints those two fields from the first row; the full script also prints the count of holiday rows.
holidays = get(
"/v1/calendars/holidays/USA/2026"
)["holidays"]
print(holidays[0]["date"], holidays[0]["name"])Release dates in Python
Read releases for economic release dates in the requested country and date window. Each release has at, period, topic and status. The response from 24 October 2026 contains 31 releases in the November 2026 window. The sample prints the first release's period and reads its at field with .get(), as the full script does.
releases = get(
"/v1/economics/calendar", countries="USA", start_date="2026-11-01", end_date="2026-11-30", limit=100
)["releases"]
print(releases[0]["period"], releases[0].get("at"))Economics in Python
Read observations for CPI periods and values, using start to request observations from a period. The CPI index for September 2026 is 336.940 in the response from 24 October 2026. Each observation has period and value; this sample reads both fields from the last returned row. Narwhal's economics data also includes inflation, GDP and yields.
cpi = get(
"/v1/economics/USA/cpi/observations", start="2025-09"
)["observations"]
print(cpi[-1]["period"], cpi[-1]["value"])Commodity spot prices in Python
Read price for the Brent spot price in USD. The response from 24 October 2026 gives Brent at 116.8809 dollars a barrel. The sample assigns that field to brent and prints it with a label. Commodity spot codes include, for example, XAU, XAG, XPT, XPD, XCU, BRENT and WTI; this request uses BRENT.
brent = get(
"/v1/commodities/spot/BRENT/USD"
)["price"]
print("Brent", brent)Exchange rates in Python
Read rates for the currencies requested against USD. Each row has a currency and rate, which the sample turns into a dictionary. The response from 24 October 2026 gives USD/EUR as 0.89342 and USD/IDR as 18107.8, both at the 23 October 2026 close. The call requests EUR and IDR together through the currencies parameter.
fx = get(
"/v1/fx/rates/USD", currencies="EUR,IDR"
)["rates"]
print({r["currency"]: r["rate"] for r in fx})Prediction-market odds in Python
Read meetings for the Federal Reserve policy path. Each meeting has resolves_at, p_cut, p_hold and p_hike. The response from 24 October 2026 gives a hold probability of 0.9188 for the 28 October 2026 meeting. This sample prints the date portion of resolves_at and the first meeting's p_hold value, using the same fields as the full script.
fed = get(
"/v1/predictions/policy-paths/federal-reserve"
)["meetings"]
print(fed[0]["resolves_at"][:10], fed[0]["p_hold"])Why do I get 401?
The Authorization header is missing or the environment variable is empty. Print whether Python has a value for NARWHAL_API_KEY with bool(os.environ.get('NARWHAL_API_KEY')). The helper builds its header from that variable, so set it in the environment where the script runs. The expected header is Authorization: Bearer YOUR_KEY, with the key value supplied by the environment variable. For a scheduled pull, use the job environment when looking for an empty variable.
How do I turn a list of rows into a table?
Use pandas.DataFrame(rows) with the list of rows you want to turn into a table. For the holiday call, that list is holidays; for release dates, it is releases. See the pandas tutorial for working with the table after you read the response rows. The CPI observations, FX rates and prediction meetings fields are also lists of rows. Select the response field in Python before passing its rows to pandas.DataFrame(rows).
Published .
