You can use Node.js and TypeScript with Narwhal to pull holidays, release dates, economics, commodity spot prices, exchange rates and prediction-market odds. Use plain fetch with your API key, then schedule the script with cron or a scheduled job on your host.
What you'll set up
Node.js provides the JavaScript environment for a script that calls the Narwhal REST API and reads its JSON responses. You will have an ES module with a shared request helper and calls for all six data areas. Narwhal uses one account and one API key across its data APIs, and REST and MCP share that key. The script here uses REST, with the key read from an environment variable.
Set your API key
- Create a free key Get a free API key at signup. The same key authenticates the requests for every data area in this script.
- Store the key in an environment file Put your key in a
.envfile under the nameNARWHAL_API_KEY. ReplaceYOUR_KEYwith your key; the script reads the value throughprocess.env.NARWHAL_API_KEY.Environment fileNARWHAL_API_KEY=YOUR_KEY - Use an ES module and the matching Node version Save the script as
pull.mjs. Node 18.17 or later has globalfetch, so the REST call needs no package. Loading the.envfile withnode --env-file=.env pull.mjsneeds Node 20.6 or later.
The request header is Authorization: Bearer YOUR_KEY. In the script, the header takes its value from the environment variable, while the base URL stays https://api.narwhalapi.com. Each call uses the same header through the shared helper.
Pull data with code
Save this code in pull.mjs. The get helper builds a URL from the base URL and path, adds the supplied query parameters, and sends a GET request with fetch. If the response is unsuccessful, it throws an error containing the status and response text. Otherwise, it returns the JSON response for the calling code to read.
Keep the helper and the area calls in the same ES module, with the base URL and header defined before the requests. The parameter object belongs to each call: the calendar call supplies country and date filters, the CPI call supplies a start period, and the FX call supplies currencies.
const API = "https://api.narwhalapi.com";
const headers = { Authorization: `Bearer ${process.env.NARWHAL_API_KEY}` };
async function get(path, params = {}) {
const url = new URL(API + path);
for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
return res.json();
}
const { holidays } = await get("/v1/calendars/holidays/USA/2026");
const { releases } = await get("/v1/economics/calendar", { countries: "USA", start_date: "2026-11-01", end_date: "2026-11-30", limit: 100 });
const { observations } = await get("/v1/economics/USA/cpi/observations", { start: "2025-09" });
const { price } = await get("/v1/commodities/spot/BRENT/USD");
const { rates } = await get("/v1/fx/rates/USD", { currencies: "EUR,IDR" });
const { meetings } = await get("/v1/predictions/policy-paths/federal-reserve");
console.log(holidays.length, holidays[0].name);
console.log(releases.length, observations.at(-1));
console.log("Brent", price, Object.fromEntries(rates.map((rate) => [rate.currency, rate.rate])));
console.log(meetings[0].resolves_at.slice(0, 10), meetings[0].p_hold);Use node --env-file=.env pull.mjs to load the key and call the API. The calls extract the response fields used by the output statements. For exchange rates, the script turns the returned currency and rate entries into an object; for the prediction response, it reads the meeting date and hold probability. The area samples that follow use this same get helper and header.
Schedule the pull
Use cron or a scheduled job on your host. With cron, the command includes the path to the environment file and the path to pull.mjs, so the scheduled script has the key in process.env.NARWHAL_API_KEY. The --env-file option still requires Node 20.6 or later when the command is scheduled.
0 7 * * * node --env-file=/path/.env /path/pull.mjsReplace /path/.env and /path/pull.mjs with the paths to your files. For more on scheduling the request, use schedule a daily data pull.
Holidays in Node.js
Request USA holidays for 2026 and read the holidays array. Each holiday has a date and name; the sample prints the array length and the first holiday name. On 24 October 2026, the response contained 11 US holidays for 2026. To use the date in your own output, read date on a holiday entry. The name field provides the holiday name associated with that date.
const { holidays } = await get(
"/v1/calendars/holidays/USA/2026"
);
console.log(holidays.length, holidays[0].name);Release dates in Node.js
Request the USA economic calendar for the November 2026 window and read releases. Each entry has at, period, topic and status, so you can read the release date alongside its topic and period. On 24 October 2026, this November 2026 window contained 31 releases. The date filters belong to the calendar request; the period field belongs to each release entry. Read the topic and status from that same entry when you need those fields in your output.
const { releases } = await get("/v1/economics/calendar", {
countries: "USA", start_date: "2026-11-01", end_date: "2026-11-30", limit: 100
});
console.log(releases.length);Economics in Node.js
Request USA CPI observations with the start parameter and read observations. Each observation has a period and value; the sample reads the final entry in the returned array. On 24 October 2026, the CPI value for the 2026-09 period was 336.940. Narwhal economics also includes inflation, GDP and yields. Keep the period with the value when you read an observation, so your output identifies which CPI period the value belongs to. The request start period is supplied as a query parameter.
const { observations } = await get(
"/v1/economics/USA/cpi/observations", { start: "2025-09" }
);
console.log(observations.at(-1));Commodity spot prices in Node.js
Request Brent in USD and read the response field price. On 24 October 2026, Brent was 116.8809 dollars a barrel. Commodity spot codes include, for example, XAU, XAG, XPT, XPD, XCU, BRENT and WTI. This sample keeps BRENT and USD in the request path. Here the code reads a single price field from the JSON response. The commodity code and currency are part of the URL passed to the shared request helper.
const { price } = await get(
"/v1/commodities/spot/BRENT/USD"
);
console.log("Brent", price);FX rates in Node.js
Request EUR and IDR with USD as the base and read rates. Each entry has currency and rate, and the decimal rates are strings. On 24 October 2026, USD/EUR was 0.89342 and USD/IDR was 18107.8. The sample uses each currency as the object property for its returned rate. The requested currencies are query parameters, while the base currency is part of the path. Read the currency field with each rate when you use the returned array directly.
const { rates } = await get(
"/v1/fx/rates/USD", { currencies: "EUR,IDR" }
);
console.log(Object.fromEntries(rates.map((rate) => [rate.currency, rate.rate])));Prediction-market odds in Node.js
Request the Federal Reserve policy path and read meetings. Each meeting has resolves_at, p_cut, p_hold and p_hike. On 24 October 2026, the Fed hold probability for 28 October 2026 was 0.9188. The sample prints the date from the first meeting entry alongside its hold probability. Use p_cut or p_hike from a meeting entry when you want its cut or hike probability. Keep resolves_at with the probability to identify the meeting it describes.
const { meetings } = await get(
"/v1/predictions/policy-paths/federal-reserve"
);
console.log(meetings[0].resolves_at.slice(0, 10), meetings[0].p_hold);Do I need TypeScript types?
Type only the response fields you read. For the FX example, the response shape is { rates: { currency: string; rate: string }[] }. Decimal values are strings, so use string for rate in your TypeScript shape.
Why is await at the top level?
ES modules allow top-level await, which is how the sample waits for each get call. Use the .mjs extension shown here, or set "type": "module" for a JavaScript ES module.
Published .
