Build a Fed decision countdown in JavaScript

A Fed decision countdown is one fetch to https://www.financecalendar.com/wp-json/fc/v1/next?series=fomc, which returns the next FOMC announcement as an ISO 8601 UTC instant, and a 1-second timer that subtracts it from now. The API is free, needs no key and allows browser requests, so the whole thing is about 40 lines of plain JavaScript.

The Finance Calendar API gives you time_utc for every scheduled event, so you never hard-code a meeting date or guess at daylight saving. This guide builds a countdown for the FOMC, then shows how to switch it to CPI, the jobs report or the Bank of England, and how to show the consensus and the result when it lands. Each code block runs as written.

1. Fetch the next decision

const API = "https://www.financecalendar.com/wp-json/fc/v1";

async function nextEvent(series) {
  const r = await fetch(`${API}/next?series=${encodeURIComponent(series)}`);
  const data = await r.json();
  if (!r.ok) throw new Error(data.error || `HTTP ${r.status}`);
  return data.next;
}

const fomc = await nextEvent("fomc");
console.log(fomc.title, fomc.time_utc);
// "FOMC Rate Decision October 2026" "2026-10-28T18:00:00+00:00"

The response as it stands today, trimmed to the fields the countdown uses:

{
  "series": "fomc",
  "series_code": "fomc",
  "country": "US",
  "next": {
    "id": "fomc-rate-decision-october-2026",
    "name": "FOMC decision",
    "title": "FOMC Rate Decision October 2026",
    "date": "2026-10-28",
    "time_utc": "2026-10-28T18:00:00+00:00",
    "time_et": "14:00",
    "starts_on": "2026-10-27",
    "status": "confirmed",
    "consensus": null,
    "prior": null,
    "actual": null,
    "unit": "% target range",
    "url": "https://www.financecalendar.com/event/fomc-rate-decision-october-2026/",
    ...
  },
  "attribution": { "source": "financecalendar.com", "terms": "...", "docs": "..." }
}

time_utc carries its offset, so new Date(fomc.time_utc) is correct in every browser. The 14:00 Eastern announcement is 18:00 UTC in October and 19:00 UTC in December after the clocks change; the API has already done that sum.

2. The countdown

<div id="fed">
  <h2 id="fed-title"></h2>
  <p id="fed-clock"></p>
  <p id="fed-meta"></p>
  <p>Data: <a href="https://www.financecalendar.com">Finance Calendar</a></p>
</div>

<script type="module">
const API = "https://www.financecalendar.com/wp-json/fc/v1";
const $ = (id) => document.getElementById(id);

function pad(n) { return String(n).padStart(2, "0"); }

function render(target) {
  const ms = target - Date.now();
  if (ms <= 0) { $("fed-clock").textContent = "Decision due now"; return false; }
  const s = Math.floor(ms / 1000);
  const d = Math.floor(s / 86400), h = Math.floor(s % 86400 / 3600), m = Math.floor(s % 3600 / 60), sec = s % 60;
  $("fed-clock").textContent = `${d}d ${pad(h)}h ${pad(m)}m ${pad(sec)}s`;
  return true;
}

const r = await fetch(`${API}/next?series=fomc`);
const { next } = await r.json();
const target = new Date(next.time_utc);

$("fed-title").textContent = next.title;
$("fed-meta").textContent = `${target.toLocaleString(undefined, { dateStyle: "full", timeStyle: "short" })}`
  + (next.consensus ? ` | Consensus: ${next.consensus}` : "")
  + (next.prior ? ` | Prior: ${next.prior}` : "");

const timer = setInterval(() => { if (!render(target)) clearInterval(timer); }, 1000);
render(target);
</script>

toLocaleString prints the date in the visitor’s own zone, so a reader in Singapore sees 02:00 on 29 October and a reader in New York sees 14:00 on 28 October, both from the same time_utc. If you want a fixed zone, pass timeZone: "America/New_York" in the options.

3. Show the result when it lands

actual is filled within the hour of the statement and status becomes released. When the countdown reaches zero, poll /next every few minutes. As soon as the row’s id changes, the previous meeting is over; fetch it from /history to show the outcome, and start the clock on the new target.

async function afterZero(previousId) {
  const { next } = await (await fetch(`${API}/next?series=fomc`)).json();
  if (next.id === previousId) return setTimeout(() => afterZero(previousId), 5 * 60 * 1000);
  const hist = await (await fetch(`${API}/history?series=fomc&limit=1`)).json();
  const last = hist.events[0];
  $("fed-meta").textContent = last.actual ? `Result: ${last.actual}` : "Result pending";
  // then start a new countdown on new Date(next.time_utc)
}

Responses are cached for 5 minutes, so there is no point polling faster than that. The last completed meeting at the time of writing, 16 September 2026, has "actual": "Raised 25bp to 3.75%-4.00% (first hike in three years)".

4. Switch to any series

Change the series value and nothing else:

  • cpi for US CPI, uk-cpi for the UK, eurozone-cpi for the euro area
  • nfp or jobs for the US jobs report
  • gdp, pce, retail-sales, ism
  • ecb, boe, boj, boc, rba, rbnz, snb, pboc

Generic names default to the US release. For another country add &country=uk or use a prefixed code. Unknown series return HTTP 404 with No upcoming event found for series "...", which the nextEvent function above turns into a thrown error. /series lists every code and alias.

For several countdowns on one page, one request to /calendar is cheaper than one per series:

const { events } = await (await fetch(`${API}/calendar?series=fomc,us-cpi,us-nfp,ecb,boe&to=2027-01-19`)).json();
const firstOfEach = Object.values(events.reduce((acc, e) => (acc[e.series_code] ??= e, acc), {}));
for (const e of firstOfEach) console.log(e.series_code, e.time_utc, e.consensus);

Rows come back soonest first, so the first row per series_code is the next occurrence.

5. Multi-day meetings

FOMC, ECB and Bank of Japan meetings run over 2 days. The row carries starts_on for the first day (27 October for the October 2026 FOMC) and time_utc for the announcement on the second. Count down to time_utc; use starts_on if you want a “meeting in progress” state the day before.

6. Server-side with Node

If your site is rendered on the server, fetch the date there and send the target timestamp to the page, so the countdown starts ticking before any client-side request. Node 18 and later has fetch built in:

// node 18+
const API = "https://www.financecalendar.com/wp-json/fc/v1";
let cache = { at: 0, next: null };

export async function nextFomc() {
  if (Date.now() - cache.at < 5 * 60 * 1000) return cache.next;
  const r = await fetch(`${API}/next?series=fomc`, { headers: { "User-Agent": "my-site/1.0" } });
  if (!r.ok) throw new Error((await r.json()).error);
  cache = { at: Date.now(), next: (await r.json()).next };
  return cache.next;
}

Render data-target="2026-10-28T18:00:00+00:00" on the element and let a 10-line client script do the ticking. The 5-minute cache matches the API’s own, so a popular page makes at most 288 requests a day however many readers it has. Add an X-API-Key header on the server if you run several such pages from one address; never ship the key to the browser.

7. Make it readable

A countdown that only shows digits is hard to use. Three small things help. Print the absolute time next to the clock, in the reader’s zone, so they can put it in a diary; toLocaleString above does that. Give the element aria-live="polite" so screen readers announce changes, but update the visible text once a minute rather than every second when the decision is more than an hour away, or the announcements never stop. And link the title to next.url, the preview page for that meeting on this site, which is where the consensus, the prior decision and the result are written up.

The row also tells you what kind of meeting it is. note carries “Summary of Economic Projections” for the quarterly meetings with the dot plot (the December 2026 meeting, for example), and starts_on marks the first day of the 2-day meeting. A line such as “Two-day meeting from 27 October; projections published with this decision” costs nothing and answers the question most readers have.

8. Rate limits and caching

Each visitor’s browser calls the API directly and the keyless limit is 2,000 requests a day per IP address, so a public page does not need a key and a single reader cannot exhaust it. Do not put a free key in client-side code. If you render server-side, cache the /next response for 5 minutes, which matches the API’s own cache, and send a key from the server if you expect more than 2,000 renders a day.

9. What can go wrong

Three failure modes are worth handling. If the API returns HTTP 404 (“No upcoming event found for series …”), the series has no confirmed future date in the snapshot, which can happen briefly between one year’s published schedule and the next; show “Next date not yet announced” rather than a broken clock. If it returns HTTP 429, the visitor’s IP address has made more than 2,000 requests today; the body names the limit and the response carries Retry-After, so fall back to a cached target if you have one. If the request fails altogether, render the page without the countdown; the fed-title and fed-meta elements above stay empty and nothing else breaks. Wrap the fetch in try and write the message to fed-meta so the failure is visible while you test.

10. Without writing code

The embeddable widget shows the coming releases with times in the visitor’s zone in 2 lines of HTML, and data-series="fomc" narrows it to the Fed. It does not count down, but for many pages a dated list is enough.

Frequently asked questions

Can I call the API from a browser?

Yes. CORS is open (Access-Control-Allow-Origin: *) on every endpoint, so fetch works from any origin without a proxy.

What time is the FOMC decision?

14:00 Eastern Time on the second day of the meeting. The API gives the matching UTC instant in time_utc, which changes between 18:00 and 19:00 UTC with daylight saving.

How do I know the date is right?

Every row carries source_url, the Federal Reserve’s own meeting calendar, and status: confirmed once the Fed has published it. The API re-reads the source every hour, and /changes records any move.

Can I show the countdown in Eastern Time regardless of the visitor?

Yes. Pass { timeZone: "America/New_York" } to toLocaleString, or display time_et from the row, which is already “14:00”.

Attribution

Data from this API is free for any use, commercial included, on one condition: a visible link to https://www.financecalendar.com near where the data appears, or on your credits page. Earnings dates are not included.

Similar Posts