# Economic Calendar Scraper - Actual, Forecast & Previous (`neverempty/economic-calendar-scraper`) Actor

Economic calendar events from TradingView for any date range since 2013: actual, forecast and previous values, the surprise versus forecast, importance, country and currency, in UTC and your time zone. Monitoring mode returns an event only when its actual is released or its forecast changes.

- **URL**: https://apify.com/neverempty/economic-calendar-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Business, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.30 / 1,000 event returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Economic Calendar Scraper - Actual, Forecast & Previous

Get the economic calendar as data: every scheduled release (Non Farm Payrolls, CPI, Fed interest rate decisions, GDP, PMIs, central bank meetings and about 150 countries' statistics) with its **actual, forecast and previous** values, the **surprise versus the forecast**, importance, country, currency and release time in UTC and in your own time zone. Any date range from 2013 on, filtered by country, currency, importance and event name.

**Monitoring mode** turns it into a release alert: run it on a schedule and it returns an event only when its **actual figure is released** or its **forecast changes**, with the earlier values.

No login, no API key, no browser.

### Where the data comes from

The events come from the public economic calendar on tradingview.com, read through the unofficial endpoint (`economic-calendar.tradingview.com/events`) that TradingView's own calendar page uses. It is not an official API and TradingView can change or stop it without notice; if that happens the run returns free rows that say what TradingView answered instead of guessing. The robots.txt of that host allows `/events`. The actual, forecast and previous values are TradingView's, passed through unchanged. **This is data, not investment advice.**

### What you can do with it

- Pull this week's high-importance events for the US, euro area, UK and Japan into a spreadsheet or a trading bot.
- Build a history of release surprises (actual minus forecast) for NFP, CPI or any indicator since 2013, to study how markets reacted.
- Get a row within minutes of a release: schedule monitoring mode every few minutes around the release time.
- Avoid trading through high-importance events: pull tomorrow's events in your local time zone.

### Quick start

**This week's high and medium importance events for the G4, in New York time**

```json
{
  "dateRange": "this_week",
  "countries": ["US", "EU", "GB", "JP"],
  "importance": ["high", "medium"],
  "timezone": "America/New_York"
}
```

**Every US payrolls and inflation release of 2025, with the surprise**

```json
{
  "startDate": "2025-01-01",
  "endDate": "2025-12-31",
  "countries": ["US"],
  "keywords": ["Non Farm Payrolls", "Inflation Rate"]
}
```

**Release alert: return US high-importance events only when the actual comes out (schedule it every 5 minutes)**

```json
{
  "dateRange": "today",
  "countries": ["US"],
  "importance": ["high"],
  "monitoringMode": true
}
```

### Input

| Field | What it does |
|---|---|
| `dateRange` | `today`, `tomorrow`, `yesterday`, `next_7_days` (default), `last_7_days`, `this_week`, `next_week`, `last_week`, `this_month`, `next_month`, `last_month`. Days are counted in `timezone`; weeks run Monday to Sunday. Ignored when `startDate` is given. |
| `startDate` | YYYY-MM-DD. Replaces `dateRange`. Any day from 2013-01-01 on; an earlier start is moved to 2013-01-01 and a free row says so. |
| `endDate` | YYYY-MM-DD, included. Leave empty for a single day. Needs `startDate`. |
| `timezone` | IANA time zone name (default `UTC`), for example `America/New_York`, `Europe/London`, `Asia/Tokyo`. It decides where each day starts and ends, and fills `localDateTime`. |
| `countries` | Two-letter codes as TradingView uses them: `US`, `GB`, `JP`, `CN`, `DE`, `CA`, `AU` and so on. `EU` is the euro area as a whole and `WW` is worldwide releases; `UK` is read as `GB`, lower case is accepted. Empty = all countries. |
| `currencies` | Keep only events in these currencies, for example `USD`, `EUR`, `JPY`. `EUR` matches the euro area and every euro country. Empty = all. |
| `importance` | Any of `high`, `medium`, `low`, as TradingView rates events. Empty = all three. |
| `keywords` | Keep only events whose title or indicator contains one of these words, ignoring case (`Inflation Rate YoY` also matches `Core Inflation Rate YoY`). Empty = all. |
| `actualFilter` | `any` (default), `with-actual` (the figure is out) or `without-actual` (no figure yet; this includes speeches and holidays, which never get one). |
| `includeDescription` | Adds TradingView's explanation of the indicator in `description`. Default off. |
| `maxResults` | With monitoring off, the most charged rows one run returns (default 1,000, up to 100,000). Oldest dates come first; a free row says from which date events were not returned. It does not limit monitoring mode. |
| `monitoringMode` | Return an event only when its actual or forecast changed since the row last returned for it. See below. |
| `resetMonitoringState` | Forget the remembered values for this set of filters, so that this run returns every matching event once again as a first check. Turn it off again afterwards. |
| `useProxy` | Default on. Requests go directly first; only if TradingView refuses (HTTP 403, 429 or 503) is the request retried through an Apify proxy. |

### Output

One row per event, `status: "ok"`:

```json
{
  "source": "tradingview-economic-calendar",
  "status": "ok",
  "eventId": "396731",
  "title": "Non Farm Payrolls",
  "indicator": "Non Farm Payrolls",
  "country": "US",
  "countryName": "United States",
  "currency": "USD",
  "importance": "high",
  "importanceCode": 1,
  "dateTimeUtc": "2026-09-04T12:30:00.000Z",
  "dateUtc": "2026-09-04",
  "timeUtc": "12:30",
  "localDateTime": "2026-09-04T08:30:00-04:00",
  "localDate": "2026-09-04",
  "localTime": "08:30",
  "timezone": "America/New_York",
  "period": "Aug",
  "referenceDate": "2026-08-31T00:00:00Z",
  "actual": 162,
  "forecast": 56,
  "previous": 21,
  "unit": null,
  "scale": "K",
  "actualRaw": 162000,
  "forecastRaw": 56000,
  "previousRaw": 21000,
  "hasActual": true,
  "surprise": 106,
  "surprisePercent": 189.29,
  "surpriseDirection": "above-forecast",
  "category": "lbr",
  "ticker": "ECONOMICS:USNFP",
  "sourceName": "Bureau of Labour Statistics",
  "sourceUrl": "http://www.bls.gov/",
  "description": null,
  "requestedRange": "2026-09-01 to 2026-09-16 (America/New_York)",
  "scrapedAt": "2026-09-17T00:30:00.000Z",
  "change": null,
  "changes": [],
  "isFirstCheck": null,
  "previousCheckedAt": null,
  "previousActual": null,
  "previousForecast": null
}
```

What the columns mean:

- `title` is the event as TradingView names it (for example `Non Farm Payrolls`, `Fed Interest Rate Decision`); `indicator` is the indicator it belongs to (`Interest Rate` for the decision, the minutes and the press conference).
- `eventId` is TradingView's id for the event; it stays the same when the figure comes out, so you can join runs on it.
- `dateTimeUtc`, `dateUtc`, `timeUtc` are the scheduled release time in UTC; `localDateTime`, `localDate`, `localTime` are the same moment in `timezone`. TradingView gives some events a time of 00:00 UTC that is not a real release time: in the measured week (all countries, 2026-09-01 to 2026-09-08), 18 of 555 events were at 00:00 UTC and 15 of those were holidays.
- `actual`, `forecast`, `previous` are the values as TradingView displays them, in `scale` (`K` thousand, `M` million, `B` billion, `T` trillion) and `unit` (such as `%` or a currency sign) when TradingView gives one. `actualRaw`, `forecastRaw`, `previousRaw` are the same values with the scale applied (NFP `162` with scale `K` is `162000`). A value TradingView does not have is `null`, never 0; the forecast was present for only 164 of the 555 events in the measured week. There is no separate "revised previous" column because TradingView does not provide one.
- `period` is the period the figure is about as TradingView writes it (`Aug`, `Q2`, `Sep/01`), and `referenceDate` the end of that period (empty for holidays and speeches).
- `hasActual` is true when the actual figure is out.
- `surprise` is `actual` minus `forecast` in the displayed scale, rounded to the decimals of the two values; `surprisePercent` is that difference as a percentage of the forecast's size (`null` when the forecast is 0); `surpriseDirection` is `above-forecast`, `below-forecast` or `in-line`. All three are `null` unless both values exist. Whether "above" is good or bad depends on the indicator (a higher unemployment rate is not good news), so the direction is not labelled as positive or negative.
- `importance` is `high`, `medium` or `low` (TradingView's `importanceCode` 1, 0, -1).
- `category` is TradingView's own category code (for example `lbr`, `prce`, `gdp`, `mny`), passed through untranslated. `ticker` is TradingView's symbol for the indicator when it has one; `sourceName` and `sourceUrl` are the agency that publishes the figure.
- `description` is filled only with `includeDescription`.
- `requestedRange` says which days were asked for and in which time zone.
- `country` is TradingView's two-letter code and `countryName` its English name (`Euro Area` for `EU`, `World` for `WW`); `currency` is the currency TradingView assigns to the event (`EUR` for the euro area and every euro country).
- `source` and `scrapedAt` say where and when the row was read.
- Monitoring columns: `change`, `changes`, `isFirstCheck`, `previousCheckedAt`, `previousActual`, `previousForecast` (see below; empty with monitoring off).

### Monitoring mode

Turn on `monitoringMode` and run the Actor on a schedule. The Actor remembers the actual and forecast of every event it returns and, on later runs, returns an event only when:

- `"actual-released"`: the actual figure was not there before and is there now;
- `"actual-changed"`: the actual figure is different from the one returned before (a correction, or TradingView removed it);
- `"forecast-changed"`: the forecast appeared, changed or disappeared.

The first run returns every matching event once as `"first-check"`, to set the baseline. `changes` lists every change (an event can have both `actual-released` and `forecast-changed`), `change` is the first of them, and `previousActual`, `previousForecast` and `previousCheckedAt` are the values in the row returned before. A run where nothing changed returns one free `no-change` row. Changes in the previous value or in the release time do not trigger a row.

Before relying on these values for monitoring, the Actor was checked for values that change between reads: an 895-event range read four times over about half a minute on 2026-09-17 gave the same events and the same values every time (0 differences).

Values are remembered per set of filters (countries, currencies, importance, keywords, actual filter), so schedules with different filters do not hide events from each other; two schedules with the same filters and different date ranges share what is remembered. Monitoring needs at least one of countries, currencies, keywords or importance. Do not run the same filters in two schedules that can run at the same time: Apify's key-value store has no atomic update, so a record written by one run can be overwritten by the other, and an event can then come back once more. If Apify restarts a run (for example when it moves the run to another server), the events of the date window that was being read can be checked, and charged as checks, a second time; rows already returned are not returned or charged again.

### Free rows that explain

Rows with any other `status` are not charged and carry a `note`:

| status | when |
|---|---|
| `no-events` | TradingView answered normally and has no events for the range and countries. |
| `no-matching-events` | TradingView returned events, but none matched the currencies, importance, keywords or actual filter. |
| `country-without-events` | A country code you gave has no events in the range (for a code TradingView did not use between September 2025 and October 2026, the note says so). |
| `range-note` | The start was moved to 2013-01-01, or the range reaches more than five weeks ahead, where TradingView may not list events yet. |
| `blocked` | TradingView refused (HTTP 403, 429 or 503) after retries; the note says which dates are missing. |
| `unreadable` | TradingView's answer could not be read after retries; the note says which dates are missing. |
| `refused` | TradingView rejected the request with HTTP 400; nothing more was asked. |
| `unreadable-events` | TradingView returned events whose id, time or country could not be read; the note gives the count. They are not returned or charged. |
| `incomplete` | More than 2,000 events share a single moment, so some of them may be missing. |
| `no-change` | Monitoring mode: nothing changed since the last rows. |
| `not-checked` | `maxResults` was reached; the note says from which date events were not returned. |
| `budget-reached` | The run's maximum total charge has no room for another row (or, in monitoring mode, another check with a change row); the note says from which date events were not read. |
| `invalid-input` | The input could not be used; the note says which field and why. |

### How it reads the calendar

TradingView returns at most 2,000 events per request, silently (asking for one year of US events on 2026-09-17 returned 2,000 events ending on 2026-03-04). The Actor therefore reads 7-day windows for all countries (the busiest 7 days of all countries between 2025-09-01 and 2026-10-25 had 694 events) and 28-day windows when you name countries, and when a window still hits 2,000 events it continues from the time of the last event it received, removing duplicates by `eventId`. The calendar starts in January 2013 and, on 2026-09-17, listed United States events up to 2026-10-21, so it covers roughly five weeks ahead. TradingView used 156 country codes between September 2025 and October 2026.

### Pricing

Pay per event:

- **$10.00 per 1,000 event rows**, charged only for rows with `status: "ok"`. Rows that explain a missing result are free.
- Monitoring mode only: **$0.30 per 1,000 event checks**, charged for every matching event that is checked, including on the first run and whether or not it changed, plus the row price for the rows returned. Example: 30 events checked every hour is 21,600 checks a month, which is **$6.48**.

A run never charges more than its maximum total charge: with monitoring off it stops before a row that would not fit, and in monitoring mode it checks only as many events as can be paid for together with a change row.

### FAQ

**Which time zone are the times in?** `dateTimeUtc` is always UTC. `localDateTime` is in the `timezone` you choose, with its UTC offset, including daylight saving time.

**Why is `actual` empty for an event in the past?** Speeches, meetings and holidays never get a figure. For releases, TradingView may fill it a little after the release time; monitoring mode returns the event when it appears.

**Why is `forecast` empty?** TradingView does not have a forecast for every event. The Actor never fills a missing value with 0.

**Can I get years of history?** Yes, any range from 2013 on in one run. One year of all countries is about 26,000 events (30,119 events were listed between 2025-09-01 and 2026-10-25, 420 days), so raise `maxResults` or filter by country.

**Is this the ForexFactory or Investing.com calendar?** No. It is TradingView's calendar. Its event names, importance ratings and forecasts can differ from other calendars.

# Actor input Schema

## `dateRange` (type: `string`):

Which days to return, counted in the time zone below. Weeks run Monday to Sunday. Ignored when From date is filled in. TradingView's calendar starts in January 2013 and lists events about five weeks ahead.

## `startDate` (type: `string`):

YYYY-MM-DD, a calendar day in the time zone below. When given, it replaces Date range. Any day from 2013-01-01 on, so past years work too (one year of all countries is about 26,000 events).

## `endDate` (type: `string`):

YYYY-MM-DD, included. Leave empty for a single day. Needs From date.

## `timezone` (type: `string`):

IANA time zone name, for example UTC, America/New\_York, Europe/London, Asia/Tokyo. It decides where each day starts and ends for the date range, and every row also shows the event time in this zone (localDateTime) next to the UTC time.

## `countries` (type: `array`):

Two-letter country codes as TradingView uses them, for example US, GB, JP, CN, DE, CA, AU. EU is the euro area as a whole and WW is worldwide releases; UK is read as GB. Lower case is accepted. Leave empty for all countries (about 150). A code with no events in the range gets a free row that says so.

## `currencies` (type: `array`):

Keep only events whose currency is one of these, for example USD, EUR, JPY (the way forex calendars group events). EUR matches the euro area and every euro country. Leave empty for all.

## `importance` (type: `array`):

Keep only these importance levels, as TradingView rates them. Leave empty for all three.

## `keywords` (type: `array`):

Keep only events whose title or indicator contains one of these words, ignoring case, for example Non Farm Payrolls, Inflation Rate, Interest Rate Decision, GDP. Leave empty for all.

## `actualFilter` (type: `string`):

Any = all events. With actual = only events whose actual figure is published. Without actual = only events with no actual figure yet; this also includes events that never get a figure, such as speeches and holidays.

## `includeDescription` (type: `boolean`):

Adds TradingView's explanation of the indicator (often several hundred characters) in the description column. Off keeps rows short.

## `maxResults` (type: `integer`):

With monitoring off, the run stops once this many charged rows have been returned, oldest dates first, and a free row says from which date events were not returned. In monitoring mode it does not limit the changes returned.

## `monitoringMode` (type: `boolean`):

Off = every matching event comes back, charged per row. On = the Actor remembers each event's actual and forecast and, on later runs, returns an event only when the actual figure was released or changed, or the forecast changed, since the row last returned for it, with the earlier values. The first run returns every matching event once to set the baseline. Needs at least one of countries, currencies, keywords or importance. **In monitoring mode every matching event that is checked costs $0.30 per 1,000 checks, changed or not**, plus the row price for the rows returned. Example: 30 events checked every hour = 21,600 checks a month = $6.48. The Actor checks only as many events as the run's maximum total charge can pay for with a check and a change row each. Values are remembered per set of filters; do not run the same filters in two schedules that can run at the same time.

## `resetMonitoringState` (type: `boolean`):

Forgets the remembered values for this set of filters, so the next monitoring run returns every matching event once again as a first check. Turn it off again after one run: left on in a schedule, every run returns every event as a first check and charges the row price for it.

## `useProxy` (type: `boolean`):

Requests go directly first. Only if TradingView answers with a refusal (HTTP 403, 429 or 503) is the request retried through an Apify proxy, and the rest of the run then uses the proxy. With it off, the refusal is retried directly and then reported in a free 'blocked' row.

## Actor input object example

```json
{
  "dateRange": "next_7_days",
  "timezone": "UTC",
  "countries": [
    "US",
    "EU",
    "GB",
    "JP"
  ],
  "importance": [
    "high",
    "medium"
  ],
  "actualFilter": "any",
  "includeDescription": false,
  "maxResults": 1000,
  "monitoringMode": false,
  "resetMonitoringState": false,
  "useProxy": true
}
```

# Actor output Schema

## `results` (type: `string`):

One row per economic calendar event: TradingView's event id, title and indicator, country and currency, importance, the release time in UTC and in the chosen time zone, the reference period, the actual, forecast and previous values with their unit and scale, the surprise (actual minus forecast) with its percentage and direction, the category, ticker and source agency; in monitoring mode what changed since the row last returned for the event and the earlier values. Ranges with no events, filters that matched nothing, refusals and limits come back as free rows that say why.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "countries": [
        "US",
        "EU",
        "GB",
        "JP"
    ],
    "importance": [
        "high",
        "medium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/economic-calendar-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "countries": [
        "US",
        "EU",
        "GB",
        "JP",
    ],
    "importance": [
        "high",
        "medium",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/economic-calendar-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "countries": [
    "US",
    "EU",
    "GB",
    "JP"
  ],
  "importance": [
    "high",
    "medium"
  ]
}' |
apify call neverempty/economic-calendar-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/economic-calendar-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/LjG2glbOd6t8cWqN9/builds/0QHEkLFpq5rBY2d15/openapi.json
