Why forecasts matter as much as the number itself
FX markets do not move on data releases — they move on surprises. When the Federal Reserve or the European Central Bank publishes new data, the market has already formed an expectation. If the print matches the consensus, the pair barely twitches. If it deviates, you get the spike.
That gap — the distance between what was expected and what arrived — is where a large share of FX volatility lives. To work with it systematically, you need access to the forecast alongside the actual observation, linked together in a way that lets you compute the surprise without contaminating your data with lookahead.
The FXMacroData predictions API is built for exactly this workflow. It surfaces forecasts from multiple source categories and links every prediction to its target announcement via a stable announcement_id, so you can join predictions with realised values, measure forecast accuracy over time, and build surprise indices for any indicator and currency combination you care about.
One thing to settle first: not every forecast is a market consensus, and many releases have no consensus from any official publisher. Which forecast classes exist for a given release, and which of them genuinely represent a consensus of forecasters, is covered in Consensus, Nowcast or Projection?. Read that before you use any of the numbers below as an expectation term.
What the predictions endpoint returns
One route exposes prediction data for a specific currency and indicator:
/v1/predictions/{currency}/{indicator}— all forecasts for a specific currency and indicator pair, with optional filters for prediction type and source.
Forecast data requires an active plan on every currency, USD included. Every paid plan returns the same forecast sources: a paid key widens the date window you can query, it does not add forecast sources.
The response groups entries by announcement. Each group carries the announcement_id — a stable, deterministic key in the form {currency}_{indicator}_{date} — followed by a predictions list where each entry records the forecast value, its source, and a human-readable label. Here is a minimal example for USD inflation:
{
"currency": "USD",
"indicator": "inflation",
"count": 2,
"prediction_count": 3,
"data": [
{
"announcement_id": "usd_inflation_2026-02-28",
"currency": "usd",
"indicator": "inflation",
"date": "2026-02-28",
"announcement_datetime": 1773232200,
"predictions": [
{
"predicted_value": 2.9,
"prediction_type": "market_consensus",
"prediction_source": "philly_fed_spf",
"prediction_source_label": "Philadelphia Fed Survey of Professional Forecasters"
}
]
},
{
"announcement_id": "usd_inflation_2026-03-31",
"currency": "usd",
"indicator": "inflation",
"date": "2026-03-31",
"predictions": [
{
"predicted_value": 2.6,
"prediction_type": "market_consensus",
"prediction_source": "philly_fed_spf",
"prediction_source_label": "Philadelphia Fed Survey of Professional Forecasters"
},
{
"predicted_value": 2.5,
"prediction_type": "imf_weo",
"prediction_source": "imf_weo",
"prediction_source_label": "IMF World Economic Outlook"
}
]
}
]
}
The announcement_id on every prediction row is identical to the one returned by /v1/announcements/{currency}/{indicator}, so a simple dictionary join is all you need to pair each forecast with its realised observation.
Prediction source categories
Nine prediction_type values are currently recognised. Understanding the difference matters for how you weight and interpret each forecast:
| prediction_type | What it is | Example sources |
|---|---|---|
market_consensus |
Compiled market poll of professional forecasters | Philadelphia Fed SPF, Reuters poll |
market_prediction |
Individual professional-forecaster point prediction | Forecaster-level disaggregated survey data |
survey |
Central-bank survey of professional forecasters | ECB Survey of Professional Forecasters |
model_nowcast |
Institutional model nowcast for a current-period release | Atlanta Fed GDPNow, New York Fed Staff Nowcast |
central_bank_forecast |
Official central-bank projection published in monetary policy statements | RBNZ Monetary Policy Statement, BoC Monetary Policy Report |
central_bank_projection |
Official central-bank staff or committee projection (e.g. FOMC SEP medians, ECB staff projections) | FOMC Summary of Economic Projections, Riksbank Monetary Policy Report |
imf_weo |
IMF World Economic Outlook projection | Published twice yearly, all supported currencies |
oecd_eo |
OECD Economic Outlook projection | Published twice yearly for OECD member currencies |
fxmacrodata |
FXMacroData blended forecast derived from stored macro history and available external forecasts | FXMacroData Blended Forecast |
Each source serves a different analytical purpose. Market consensus is the short-horizon market expectation that drives immediate FX reaction on release day. Central-bank forecasts reflect the institution's own outlook and are useful for tracking how much the bank's projections evolve between meetings. IMF WEO forecasts provide a neutral multi-year baseline and are consistent across all 22 covered currencies, making them useful for cross-currency comparisons.
Building a surprise index in Python
The most direct application of the predictions API is computing a release surprise index — the deviation of the actual print from the market expectation, expressed in standard deviations of historical surprises. Here is a complete example for US Non-Farm Payrolls. The NFP endpoint serves the payroll employment level in persons (BLS publishes it in thousands), so the Philadelphia Fed SPF consensus used here is the quarterly-average payroll-employment level rather than the headline one-month payroll-change poll.
import requests
import statistics
BASE = "https://api.fxmacrodata.com"
# Fetch predictions — forecast values require an API key, USD included
preds_resp = requests.get(
f"{BASE}/v1/predictions/usd/non_farm_payrolls",
params={
"prediction_type": "market_consensus",
"prediction_source": "philly_fed_spf",
},
headers={"X-API-Key": "YOUR_API_KEY"},
timeout=30,
).json()
# Fetch actuals
actuals_resp = requests.get(
f"{BASE}/v1/announcements/usd/non_farm_payrolls",
params={"start_date": "2020-01-01"},
timeout=30,
).json()
# Index actuals by announcement_id
actuals = {row["announcement_id"]: row["val"] for row in actuals_resp["data"]}
# Compute surprises (actual - consensus)
surprises = []
for group in preds_resp["data"]:
aid = group["announcement_id"]
actual = actuals.get(aid)
if actual is None:
continue
# Use the first (or only) prediction in the group
forecast = group["predictions"][0]["predicted_value"]
surprises.append({
"date": group["date"],
"actual": actual,
"forecast": forecast,
"surprise": actual - forecast,
})
# Normalise to z-scores
values = [s["surprise"] for s in surprises]
mean = statistics.mean(values)
stdev = statistics.stdev(values)
for s in surprises:
s["z_score"] = (s["surprise"] - mean) / stdev
# Print recent surprises
for s in surprises[-6:]:
direction = "↑ beat" if s["z_score"] > 0 else "↓ miss"
print(f"{s['date']}: actual={s['actual']:,.0f} forecast={s['forecast']:,.0f} "
f"surprise={s['surprise']:+,.0f} z={s['z_score']:+.2f} {direction}")
The output gives you a time series of normalised surprises that can be directly correlated with USD spot moves on release days to quantify the surprise-to-FX transmission coefficient for any given indicator.
Comparing central-bank forecasts to market consensus
A useful signal in rate-sensitive currency pairs is the divergence between what the central bank projects and what the market expects. When the two converge, it often signals that the market has fully digested the bank's guidance. When they diverge, it can indicate either a credibility gap or a genuine information asymmetry between the bank and the buy side.
You can retrieve both forecast types in a single request and compute the divergence directly:
import requests
# Fetch all prediction types for EUR inflation together
resp = requests.get(
"https://api.fxmacrodata.com/v1/predictions/eur/inflation",
headers={"X-API-Key": "YOUR_API_KEY"},
timeout=30,
).json()
# For each announcement group, compare central_bank_forecast vs market_consensus
for group in resp["data"]:
preds_by_type = {p["prediction_type"]: p for p in group["predictions"]}
cb = preds_by_type.get("central_bank_forecast")
mkt = preds_by_type.get("market_consensus")
if cb and mkt:
divergence = cb["predicted_value"] - mkt["predicted_value"]
print(f"{group['date']}: CB={cb['predicted_value']:.2f}% "
f"Market={mkt['predicted_value']:.2f}% "
f"Divergence={divergence:+.2f}pp "
f"({cb['prediction_source_label']})")
Positive divergence (the central bank projects higher inflation than the market) is typically a hawkish signal. The bank either has non-public information or is deliberately signalling concern about above-target inflation. Sustained positive divergence has historically preceded surprise hikes in several currency pairs tracked on the release calendar.
Cross-currency forecast comparison with IMF WEO
Because IMF WEO projections exist for every covered currency and use a consistent methodology, they are well-suited to cross-currency comparisons. The following snippet pulls GDP forecasts for the four major commodity-linked currencies and ranks them by expected growth:
import requests
API_KEY = "YOUR_API_KEY"
CURRENCIES = ["aud", "cad", "nzd", "brl"]
forecasts = {}
for ccy in CURRENCIES:
resp = requests.get(
f"https://api.fxmacrodata.com/v1/predictions/{ccy}/gdp",
params={"prediction_type": "imf_weo"},
headers={"X-API-Key": API_KEY},
timeout=30,
).json()
if resp.get("data"):
# Take the most recent forecast
latest = resp["data"][-1]
forecasts[ccy.upper()] = latest["predictions"][0]["predicted_value"]
# Rank by projected growth
ranked = sorted(forecasts.items(), key=lambda x: x[1], reverse=True)
print("IMF WEO GDP forecast ranking:")
for rank, (ccy, val) in enumerate(ranked, 1):
print(f" {rank}. {ccy}: {val:.1f}% YoY")
This kind of ranking feeds naturally into relative-value frameworks for commodity-currency pairs like AUD/CAD or NZD/AUD, where growth differentials are a primary driver of medium-term carry positioning.
Filtering by prediction source
When a single announcement has multiple predictions from different sources, you may want to isolate a specific one. Use the prediction_source query parameter with the stable source slug:
# Philadelphia Fed Survey of Professional Forecasters only
curl -H "X-API-Key: YOUR_API_KEY" "https://api.fxmacrodata.com/v1/predictions/usd/inflation?prediction_source=philly_fed_spf"
# ECB Survey of Professional Forecasters
curl -H "X-API-Key: YOUR_API_KEY" "https://api.fxmacrodata.com/v1/predictions/eur/inflation?prediction_source=ecb_spf"
# RBNZ Monetary Policy Statement
curl -H "X-API-Key: YOUR_API_KEY" "https://api.fxmacrodata.com/v1/predictions/nzd/inflation?prediction_source=rbnz_mps"
# IMF World Economic Outlook
curl -H "X-API-Key: YOUR_API_KEY" "https://api.fxmacrodata.com/v1/predictions/usd/inflation?prediction_source=imf_weo"
The prediction_source_label field in the response gives the full human-readable name of the source, suitable for chart labels and report footnotes. Use the stable prediction_source slug for filtering and programmatic joins.
Using the release calendar for upcoming forecasts
The release calendar surfaces upcoming announcement dates for all covered currencies. When you combine calendar data with the predictions endpoint, you can build a forward-looking dashboard that shows the current consensus for each upcoming release.
import requests
from datetime import date
API_KEY = "YOUR_API_KEY"
BASE = "https://api.fxmacrodata.com"
# Get upcoming USD releases
calendar = requests.get(f"{BASE}/v1/calendar/usd").json()
upcoming = [row for row in calendar["data"] if row.get("release")]
# For each upcoming release, fetch the current consensus prediction
for release in upcoming[:5]:
indicator = release["release"]
preds = requests.get(
f"{BASE}/v1/predictions/usd/{indicator}",
params={"prediction_type": "market_consensus"},
headers={"X-API-Key": API_KEY},
timeout=30,
).json()
if preds.get("data"):
latest_pred = preds["data"][-1]
if latest_pred.get("predictions"):
val = latest_pred["predictions"][0]["predicted_value"]
print(f"{indicator}: consensus={val}")
Point-in-time discipline: only use what was knowable
A forecast is valid for a backtest only if it existed before the release it predicts. Every prediction row carries a generated_at timestamp, and the acceptance rule is strict: keep the row only when generated_at is earlier than the announcement's announcement_datetime. FXMacroData enforces this structurally — the prediction store refuses to persist any forecast generated at or after the release, so the API cannot hand you a look-ahead row even by accident. Keep pre_release_only=true (the default) and you can re-verify the rule independently on every row.
USD pre-release forecast depth by event family (2013 → today)
The verified USD picture for point-in-time research: CPI and Core CPI carry Cleveland Fed nowcasts for every monthly release from the August 2013 reference period onward, on both the year-over-year and month-over-month series. The unemployment rate carries FOMC SEP median projection vintages for every year-end release from 2015 (with the NY Fed's market-expectations survey adding vintages from 2023), FOMC rate decisions have sparser source-labelled coverage starting in 2023, and historical event-specific market consensus is not available for Non-Farm Payrolls, PPI, or retail sales. The full picture — every currency, every source, and verified history depth — lives on the forecast coverage matrix, and the step-by-step research workflow is in the point-in-time backtesting guide.
When you pull history, always pass explicit start_date and end_date: the endpoint's default window is only about the last six months, so an unfiltered query over an old study period returns a near-empty result that is easy to misread as missing data.
Endpoint reference summary
Full parameter documentation is available on the API reference page. The table below gives a quick reference for the two predictions routes:
| Parameter | Type | Description |
|---|---|---|
currency | path | 3-letter currency code. An API key is required on every currency, USD included. |
indicator | path or query | Indicator slug (e.g. inflation, policy_rate, gdp). Path in the /{currency}/{indicator} route; optional query on /{currency}. |
prediction_type | query | Legacy category: market_consensus, market_prediction, model_nowcast, survey, central_bank_forecast, central_bank_projection, imf_weo, oecd_eo, fxmacrodata. Prefer prediction_class, which merges the values naming the same instrument. See the class reference. |
prediction_source | query | Stable source slug (e.g. philly_fed_spf, ecb_spf, imf_weo). |
start_date / end_date | query | Period date range (YYYY-MM-DD). Defaults to roughly the last 6 months through the next quarter — pass explicit dates for historical research. |
X-API-Key | header | API key from any active plan. Required for all currencies. |
The predictions API is designed to sit alongside the inflation, policy rate, and other indicator overview pages — where cross-currency comparisons are most useful — and the release calendar, where the timing of upcoming announcements meets the forecasts that precede them.