DEV Community

Automate FOMC and CPI Alerts with n8n and a Free Economic Calendar API

If you trade - or just hold anything denominated in dollars - you already know the drill: FOMC decisions, CPI prints, and NFP releases move markets in seconds, and the only warning you get is whatever tab you happened to leave open. This post walks through a small n8n workflow that polls an economic calendar every morning and pushes high-impact USD events straight to Telegram. Total build time: about 15 minutes. Total cost: $0 on free tiers.

The problem with checking ForexFactory manually

ForexFactory's calendar is the de facto standard for retail traders, but it's built for humans, not machines:

  • No official API. The calendar is rendered HTML with timezone handling done client-side. There's an unofficial weekly XML feed, but it's coarse, cached, and has broken without notice before.
  • Manual checking doesn't scale. You'll check it religiously for a week, then miss the one CPI print that mattered because it landed on a day you didn't look.
  • Scraping it yourself is annoying. The site uses Cloudflare, session-dependent timezone cookies, and markup that shifts often enough that a naive scraper needs regular babysitting.

You could pay for a commercial economic calendar API - most start around $30-100/month for the tier that includes forecast/previous values. That's overkill if all you want is "ping me when something big is scheduled today."

The actor

Disclosure up front: I built the actor used in this tutorial, so take the recommendation with that in mind.

economic-calendar-pro is an Apify actor that scrapes the ForexFactory calendar and returns clean JSON. It handles the annoying parts (timezones, session cookies, markup drift) and gives you filters for currency and impact level, plus optional webhook alerts.

Each event comes out looking like this:

{
  "title": "CPI m/m",
  "country": "USD",
  "currency": "USD",
  "impact": "High",
  "date_utc": "2026-08-12T12:30:00Z",
  "forecast": "0.2%",
  "previous": "0.3%",
  "actual": "",
  "week": "this",
  "source": "forexfactory"
}

Because it runs on Apify, you don't host anything - you call one HTTP endpoint and get dataset items back synchronously.

What we're building

Schedule (weekdays 07:00) โ†’ HTTP Request (run actor, get this week's USD high-impact events) โ†’ Filter (belt-and-suspenders: impact == high, currency == USD) โ†’ Telegram (one message per event)

If there are no qualifying events, the filter drops everything and nothing gets sent. Quiet days stay quiet.

Prerequisites

  • An n8n instance (cloud or self-hosted - docker run -p 5678:5678 n8nio/n8n works fine)
  • A free Apify account - grab your API token from Settings โ†’ Integrations
  • A Telegram bot: message @BotFather, send /newbot, save the token. Then message your bot once and get your chat ID from https://api.telegram.org/bot<TOKEN>/getUpdates

Step 1: Schedule Trigger

Add a Schedule Trigger node. Switch it to a cron expression:

0 7 * * 1-5

That's 07:00 every weekday, which comfortably beats the 08:30 ET releases (CPI, NFP) and afternoon FOMC statements. Set your instance timezone in n8n settings if you haven't - cron runs in instance time.

Step 2: Call the actor with an HTTP Request node

Add an HTTP Request node (typeVersion 4.x):

  • Method: POST
  • URL: https://api.apify.com/v2/acts/ichigowa~economic-calendar-pro/run-sync-get-dataset-items
  • Headers:
    • Authorization: Bearer YOUR_APIFY_TOKEN
    • Content-Type: application/json
  • Body (JSON):
{
  "weeks": ["this"],
  "currencies": ["USD"],
  "impacts": ["High"]
}

The run-sync-get-dataset-items endpoint is the useful one here: it starts the actor, waits for it to finish, and returns the dataset items directly in the response body. No polling for run status, no second request to fetch results.

Bump the node's timeout to ~120 seconds since a cold actor run can take 30-60s.

Hit Execute step. On a day with a CPI print you'll get something like:

[
  {
    "title": "CPI m/m",
    "currency": "USD",
    "impact": "High",
    "date_utc": "2026-08-12T12:30:00Z",
    "forecast": "0.2%",
    "previous": "0.3%"
  },
  {
    "title": "Core CPI m/m",
    "currency": "USD",
    "impact": "High",
    "date_utc": "2026-08-12T12:30:00Z",
    "forecast": "0.3%",
    "previous": "0.3%"
  }
]

n8n automatically splits the array into one item per event, which is exactly what we want downstream.

Step 3: Filter (defense in depth)

The actor already filtered server-side, but I like a Filter node here anyway - it protects you if you later widen the actor input to all currencies and forget this workflow feeds Telegram.

Two conditions, combined with AND:

  • {{ $json.impact }} equals High
  • {{ $json.currency }} equals USD

Cheap insurance, zero cost.

Step 4: Telegram message

Add a Telegram node, operation Send Message, using the bot credentials you created. Set the chat ID, enable Markdown parse mode, and use:

๐Ÿ”” *{{ $json.title }}*
Currency: {{ $json.currency }} | Impact: {{ $json.impact }}
Time (UTC): {{ $json.date_utc }}
Forecast: {{ $json.forecast || 'n/a' }} | Previous: {{ $json.previous || 'n/a' }}

Because the filter outputs one item per event, you get one message per event. On FOMC day that's typically two or three messages (rate decision, statement, press conference); most days it's zero or one.

Run it

Execute the whole workflow once manually to sanity-check, then toggle it Active.

Sample output in Telegram:

๐Ÿ”” Consumer Price Index (CPI) m/m
Currency: USD | Impact: High
Time (UTC): 2026-08-12T12:30:00Z
Forecast: 0.2% | Previous: 0.3%

What it costs

  • n8n: free self-hosted; cloud starts at ~โ‚ฌ20/month but this workflow fits in any tier.
  • Apify: free plan includes $5 of monthly platform credit. One daily run of this actor uses a few cents' worth - a weekday-morning schedule stays comfortably inside the free tier. No credit card needed.
  • Telegram: free.

Troubleshooting

A few failure modes I've seen people hit with this exact setup:

  • HTTP 401 from Apify. The header must be literally Authorization: Bearer apify_api_xxx... - a common mistake is pasting the token without the Bearer prefix, or using the account password instead of the API token.
  • HTTP 408 / timeout on the sync endpoint. run-sync-get-dataset-items caps waits at 300 seconds. If the actor run exceeds your HTTP node timeout, raise the node timeout first; the actor itself rarely takes over a minute.
  • Empty array when you expected events. The weeks parameter fetches whole calendar weeks ("this", "next", "last"), so an empty result usually means your filters were too tight - e.g. impacts: ["High"] on a week with only medium-impact USD events. Impact values are capitalized: High, Medium, Low, Holiday.
  • Telegram sends nothing, no error. Usually the bot was never messaged first (bots can't initiate chats), or the chat ID is a group ID missing the leading -.
  • Duplicate alerts after editing. If you re-activate a workflow n8n may fire a catch-up execution depending on your settings; check Workflow Settings โ†’ Timezone/Execution if you get a surprise 2 a.m. message.

Variations worth trying

  • Wider net: set "currencies": ["USD", "EUR", "GBP"] and drop the filter's currency condition.
  • Same-hour warning: add a second schedule that runs every 30 minutes, and a Filter condition like {{ $json.date_utc }} within the next hour, so you get a nudge right before the release rather than at breakfast.
  • Different sink: swap the Telegram node for Discord/Slack - the actor output doesn't change.
  • Push instead of pull: the actor supports webhook alerts natively if you'd rather skip the schedule entirely and use an n8n Webhook trigger.

Links

Questions or broken edge cases, drop a comment - the actor is actively maintained and issue reports genuinely help.

Comments

No comments yet. Start the discussion.