FRED - Federal Reserve Economic Data: API Introduction
FRED (Federal Reserve Economic Data) is maintained by the Federal Reserve Bank of St. Louis. It provides over 800,000 U.S. and international economic time series covering rates, inflation, employment, GDP, trade, and commodities - all accessible via a free, documented REST API.
This notebook covers the FRED API structure, demonstrates the most commonly used series for macroeconomic research, and shows commodity price data available through the same interface.
API access: Register for a free key at fred.stlouisfed.org/docs/api/api_key.html.
This notebook readsFRED_API_KEYfrom a.envfile in the working directory.
| Resource | Link |
|---|---|
| FRED website | fred.stlouisfed.org |
| API documentation | fred.stlouisfed.org/docs/api/fred |
| Series browser | fred.stlouisfed.org/categories |
| Python client | pip install fredapi |
import os
import requests
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
import warnings
warnings.filterwarnings('ignore')
from dotenv import load_dotenv
from fredapi import Fred # pip install fredapi
from IPython.display import display, HTML
load_dotenv()
FRED_API_KEY = os.getenv('FRED_API_KEY', '').strip()
if not FRED_API_KEY:
raise EnvironmentError('FRED_API_KEY not found in .env - see fred.stlouisfed.org/docs/api/api_key.html')
fred = Fred(api_key=FRED_API_KEY)
BASE_URL = 'https://api.stlouisfed.org/fred'
OBS_START = '2015-01-01'
FL_BLUE = '#2563eb'
FL_SLATE = '#64748b'
FL_AMBER = '#f59e0b'
FL_GREEN = '#16a34a'
FL_RED = '#ef4444'
FL_BG = '#ffffff'
FL_GRID = '#e2e8f0'
FL_TEXT = '#0f172a'
FL_TEXT2 = '#334155'
FL_BORDER = '#e2e8f0'
matplotlib.rcParams.update({
'figure.facecolor': FL_BG, 'axes.facecolor': FL_BG,
'axes.edgecolor': FL_BORDER,'axes.labelcolor': FL_TEXT2,
'axes.spines.top': False, 'axes.spines.right': False,
'axes.grid': True, 'grid.color': FL_GRID,
'grid.linewidth': 0.7, 'xtick.color': FL_TEXT2,
'ytick.color': FL_TEXT2, 'xtick.labelsize': 10,
'ytick.labelsize': 10, 'axes.labelsize': 11,
'axes.titlesize': 12, 'axes.titlecolor': FL_TEXT,
'axes.titlepad': 12, 'legend.frameon': False,
'legend.fontsize': 10, 'figure.dpi': 300,
'savefig.bbox': 'tight', 'font.family': 'sans-serif',
'font.sans-serif': ['Inter', 'Helvetica Neue', 'Arial', 'DejaVu Sans'],
})
def fetch(series_id, start=OBS_START):
info = fred.get_series_info(series_id)
values = fred.get_series(series_id, observation_start=start)
df = values.dropna().reset_index()
df.columns = ['date', 'value']
df['date'] = pd.to_datetime(df['date'])
return df, info
def series_meta_table(info):
rows = pd.DataFrame([{
'Series ID': info.name,
'Title': info.title,
'Frequency': info.frequency,
'Units': info.units,
'Seasonal adj.': info.seasonal_adjustment,
'Observation start': str(info.observation_start)[:10],
'Observation end': str(info.observation_end)[:10],
'Last updated': str(info.last_updated)[:10],
}]).T.rename(columns={0: 'Value'})
rows.index.name = 'Field'
return rows.reset_index()
#print(f'FRED API key loaded ({len(FRED_API_KEY)} chars)')
_test = fred.get_series('FEDFUNDS', observation_start='2024-01-01')
print(f'Connection OK FEDFUNDS latest: {_test.dropna().iloc[-1]:.2f}%')
Connection OK FEDFUNDS latest: 3.64%
FRED API structure
FRED organises all series into a category tree. The eight root categories contain thousands of sub-categories, each holding individual series. Series are identified by a short string ID (e.g. FEDFUNDS, CPIAUCSL, DGS10).
The three core API endpoints used in this notebook:
fred/series/observations: fetch the actual data values for a series IDfred/series: fetch metadata (title, frequency, units, date range)fred/category/children: browse the category hierarchy
The fredapi Python client wraps these into fred.get_series() and fred.get_series_info().
ROOT_CATEGORIES = {
'Money, Banking, & Finance': 32991,
'Population, Employment, & Labor Markets': 10,
'National Accounts': 32992,
'Production & Business Activity': 1,
'Prices': 32455,
'International Data': 32263,
'U.S. Regional Data': 3008,
'Academic Data': 33060,
}
r = requests.get(
f'{BASE_URL}/category/children',
params={'category_id': 32455, 'api_key': FRED_API_KEY, 'file_type': 'json'},
timeout=15
)
price_subcats = r.json().get('categories', [])
cats_df = pd.DataFrame([
{'Category': name, 'Root ID': cid}
for name, cid in ROOT_CATEGORIES.items()
])
price_subcats_df = pd.DataFrame([
{
'Category ID': cat.get('id'),
'Category name': cat.get('name')
}
for cat in price_subcats
])
print('FRED root categories')
display(cats_df)
print('Sub-categories of Prices (ID 32455)')
display(price_subcats_df)
FRED root categories
| Category | Root ID | |
|---|---|---|
| 0 | Money, Banking, & Finance | 32991 |
| 1 | Population, Employment, & Labor Markets | 10 |
| 2 | National Accounts | 32992 |
| 3 | Production & Business Activity | 1 |
| 4 | Prices | 32455 |
| 5 | International Data | 32263 |
| 6 | U.S. Regional Data | 3008 |
| 7 | Academic Data | 33060 |
Sub-categories of Prices (ID 32455)
| Category ID | Category name | |
|---|---|---|
| 0 | 32217 | Commodities |
| 1 | 9 | Consumer Price Indexes (CPI and PCE) |
| 2 | 33913 | Cryptocurrencies |
| 3 | 4 | Employment Cost Index |
| 4 | 33717 | Health Care Indexes |
| 5 | 32261 | House Price Indexes |
| 6 | 31 | Producer Price Indexes (PPI) |
| 7 | 32220 | Trade Indexes |
Interest rates
The Federal Funds Rate and the 10-Year Treasury yield are the two most-watched interest rate series in macro research. The Fed Funds Rate is the overnight policy rate set by the FOMC. The 10-Year Treasury yield reflects long-term growth and inflation expectations.
| Series ID | Description | Frequency |
|---|---|---|
FEDFUNDS |
Federal Funds Effective Rate | Monthly |
DGS10 |
10-Year Treasury Constant Maturity | Daily |
fed_df, fed_info = fetch('FEDFUNDS')
t10_df, t10_info = fetch('DGS10')
plt.figure(figsize=(8, 4.5))
plt.fill_between(fed_df['date'], fed_df['value'], alpha=0.12, color=FL_BLUE)
plt.plot(fed_df['date'], fed_df['value'], color=FL_BLUE, linewidth=1.6)
plt.ylabel('Rate (%)')
plt.title(f'Federal Funds Effective Rate (FEDFUNDS) from {OBS_START[:4]} to present')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y:.2f}%')
)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(t10_df['date'], t10_df['value'], alpha=0.12, color=FL_SLATE)
plt.plot(t10_df['date'], t10_df['value'], color=FL_SLATE, linewidth=1.2)
plt.ylabel('Yield (%)')
plt.title('10-Year Treasury Constant Maturity (DGS10), daily')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y:.2f}%')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
fed_meta_df = series_meta_table(fed_info)
display(fed_meta_df)
| Field | Value | |
|---|---|---|
| 0 | Series ID | None |
| 1 | Title | Federal Funds Effective Rate |
| 2 | Frequency | Monthly |
| 3 | Units | Percent |
| 4 | Seasonal adj. | Not Seasonally Adjusted |
| 5 | Observation start | 1954-07-01 |
| 6 | Observation end | 2026-04-01 |
| 7 | Last updated | 2026-05-01 |
Inflation and prices
CPI (Consumer Price Index) measures the average change in prices paid by urban consumers. The yearly percentage change is the standard inflation measure quoted by the Fed.
| Series ID | Description | Frequency |
|---|---|---|
CPIAUCSL |
CPI - All Urban Consumers, All Items | Monthly |
FPCPITOTLZGUSA |
Inflation, consumer prices (% annual) | Annual |
cpi_df, cpi_info = fetch('CPIAUCSL')
inf_df, inf_info = fetch('FPCPITOTLZGUSA', start='2000-01-01')
cpi_df = cpi_df.set_index('date').sort_index()
cpi_df['yoy'] = cpi_df['value'].pct_change(12) * 100
cpi_df = cpi_df.dropna().reset_index()
pos = cpi_df['yoy'] >= 0
plt.figure(figsize=(8, 4.5))
plt.bar(
cpi_df.loc[pos, 'date'],
cpi_df.loc[pos, 'yoy'],
color=FL_RED,
alpha=0.75,
width=20,
label='Positive YoY'
)
plt.bar(
cpi_df.loc[~pos, 'date'],
cpi_df.loc[~pos, 'yoy'],
color=FL_BLUE,
alpha=0.75,
width=20,
label='Negative YoY'
)
plt.axhline(0, color=FL_BORDER, linewidth=0.8)
plt.axhline(2, color=FL_AMBER, linewidth=1.2, linestyle='--', label='2% target')
plt.ylabel('YoY change (%)')
plt.title('CPI year over year inflation (CPIAUCSL), monthly')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y:.1f}%')
)
plt.legend(fontsize=9)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.plot(
inf_df['date'],
inf_df['value'],
color=FL_AMBER,
linewidth=1.8,
marker='o',
markersize=4
)
plt.axhline(2, color=FL_BLUE, linewidth=1.2, linestyle='--', label='2% target')
plt.ylabel('Inflation (%)')
plt.title('Annual consumer price inflation (FPCPITOTLZGUSA)')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y:.1f}%')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.legend(fontsize=9)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
Labour market
Unemployment and nonfarm payrolls are the two primary labour market indicators reported monthly. Together they determine the direction of Fed policy alongside inflation.
| Series ID | Description | Frequency |
|---|---|---|
UNRATE |
Unemployment Rate (seasonally adjusted) | Monthly |
PAYEMS |
All Employees, Total Nonfarm (thousands) | Monthly |
unrate_df, unrate_info = fetch('UNRATE')
payems_df, payems_info = fetch('PAYEMS')
payems_df = payems_df.set_index('date').sort_index()
payems_df['mom'] = payems_df['value'].diff()
payems_df = payems_df.dropna().reset_index()
plt.figure(figsize=(8, 4.5))
plt.fill_between(unrate_df['date'], unrate_df['value'], alpha=0.12, color=FL_RED)
plt.plot(unrate_df['date'], unrate_df['value'], color=FL_RED, linewidth=1.6)
plt.ylabel('Unemployment rate (%)')
plt.title('U.S. unemployment rate (UNRATE), seasonally adjusted')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y:.1f}%')
)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
pos_p = payems_df['mom'] >= 0
plt.figure(figsize=(8, 4.5))
plt.bar(
payems_df.loc[pos_p, 'date'],
payems_df.loc[pos_p, 'mom'],
color=FL_GREEN,
alpha=0.8,
width=20,
label='Jobs added'
)
plt.bar(
payems_df.loc[~pos_p, 'date'],
payems_df.loc[~pos_p, 'mom'],
color=FL_RED,
alpha=0.8,
width=20,
label='Jobs lost'
)
plt.axhline(0, color=FL_BORDER, linewidth=0.8)
plt.ylabel('Monthly change (thousands)')
plt.title('Nonfarm payrolls month over month change (PAYEMS)')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'{y/1000:.0f}M' if abs(y) >= 1000 else f'{y:.0f}k')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.legend(fontsize=9)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
recent = unrate_df.tail(8).copy()
recent['date'] = recent['date'].dt.strftime('%Y-%m-%d')
recent.columns = ['Date', 'Unemployment rate (%)']
recent['Unemployment rate (%)'] = recent['Unemployment rate (%)'].map(lambda v: f'{v:.1f}%')
print('UNRATE recent observations')
display(recent)
UNRATE recent observations
| Date | Unemployment rate (%) | |
|---|---|---|
| 127 | 2025-08-01 | 4.3% |
| 128 | 2025-09-01 | 4.4% |
| 129 | 2025-11-01 | 4.5% |
| 130 | 2025-12-01 | 4.4% |
| 131 | 2026-01-01 | 4.3% |
| 132 | 2026-02-01 | 4.4% |
| 133 | 2026-03-01 | 4.3% |
| 134 | 2026-04-01 | 4.3% |
GDP and activity indicators
Real GDP and retail sales measure economic output and consumer demand. These series are fetched and the response structure is shown - the same pattern applies to any series ID in FRED.
| Series ID | Description | Frequency |
|---|---|---|
GDPC1 |
Real Gross Domestic Product (billions, chained 2017$) | Quarterly |
A939RX0Q048SBEA |
Real GDP per Capita | Quarterly |
RSXFSN |
Advance Retail Sales: Retail Trade (millions $) | Monthly |
UMDMNO |
Manufacturers' New Orders: Durable Goods (millions $) | Monthly |
gdp_df, gdp_info = fetch('GDPC1')
gdppc_df, gdppc_info = fetch('A939RX0Q048SBEA')
retail_df, retail_info = fetch('RSXFSN')
durable_df, durable_info = fetch('UMDMNO')
plt.figure(figsize=(8, 4.5))
plt.fill_between(gdp_df['date'], gdp_df['value'], alpha=0.10, color=FL_BLUE)
plt.plot(gdp_df['date'], gdp_df['value'], color=FL_BLUE, linewidth=1.4)
plt.title(gdp_info.title[:55] + ('...' if len(gdp_info.title) > 55 else ''))
plt.ylabel(gdp_info.units[:30])
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y/1000:.0f}T')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(gdppc_df['date'], gdppc_df['value'], alpha=0.10, color=FL_SLATE)
plt.plot(gdppc_df['date'], gdppc_df['value'], color=FL_SLATE, linewidth=1.4)
plt.title(gdppc_info.title[:55] + ('...' if len(gdppc_info.title) > 55 else ''))
plt.ylabel(gdppc_info.units[:30])
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y:,.0f}')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(retail_df['date'], retail_df['value'], alpha=0.10, color=FL_GREEN)
plt.plot(retail_df['date'], retail_df['value'], color=FL_GREEN, linewidth=1.4)
plt.title(retail_info.title[:55] + ('...' if len(retail_info.title) > 55 else ''))
plt.ylabel(retail_info.units[:30])
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y/1000:.0f}B')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(durable_df['date'], durable_df['value'], alpha=0.10, color=FL_AMBER)
plt.plot(durable_df['date'], durable_df['value'], color=FL_AMBER, linewidth=1.4)
plt.title(durable_info.title[:55] + ('...' if len(durable_info.title) > 55 else ''))
plt.ylabel(durable_info.units[:30])
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y/1000:.0f}B')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
gdp_meta_df = pd.DataFrame([
{'Field': 'id', 'Value': gdp_info.name},
{'Field': 'title', 'Value': gdp_info.title},
{'Field': 'frequency', 'Value': gdp_info.frequency},
{'Field': 'units', 'Value': gdp_info.units},
{'Field': 'seasonal_adjustment', 'Value': gdp_info.seasonal_adjustment},
{'Field': 'observation_start', 'Value': str(gdp_info.observation_start)[:10]},
{'Field': 'observation_end', 'Value': str(gdp_info.observation_end)[:10]},
{'Field': 'last_updated', 'Value': str(gdp_info.last_updated)[:10]},
])
gdp_last4_df = gdp_df.tail(4).copy()
gdp_last4_df['date'] = gdp_last4_df['date'].dt.strftime('%Y-%m-%d')
print('GDPC1 metadata')
display(gdp_meta_df)
print('GDPC1 last 4 observations')
display(gdp_last4_df)
GDPC1 metadata
| Field | Value | |
|---|---|---|
| 0 | id | NaN |
| 1 | title | Real Gross Domestic Product |
| 2 | frequency | Quarterly |
| 3 | units | Billions of Chained 2017 Dollars |
| 4 | seasonal_adjustment | Seasonally Adjusted Annual Rate |
| 5 | observation_start | 1947-01-01 |
| 6 | observation_end | 2026-01-01 |
| 7 | last_updated | 2026-04-30 |
GDPC1 last 4 observations
| date | value | |
|---|---|---|
| 41 | 2025-04-01 | 23770.976 |
| 42 | 2025-07-01 | 24026.834 |
| 43 | 2025-10-01 | 24055.749 |
| 44 | 2026-01-01 | 24174.527 |
Energy commodities
WTI and Brent are the two global oil benchmark prices. Both are available as daily series on FRED.
| Series ID | Description | Frequency |
|---|---|---|
DCOILWTICO |
Crude Oil Prices: WTI - Cushing, Oklahoma ($/barrel) | Daily |
DCOILBRENTEU |
Crude Oil Prices: Brent - Europe ($/barrel) | Daily |
DHHNGSP |
Henry Hub Natural Gas Spot Price ($/MMBtu) | Daily |
wti_df, wti_info = fetch('DCOILWTICO')
brent_df, brent_info = fetch('DCOILBRENTEU')
ng_df, ng_info = fetch('DHHNGSP')
plt.figure(figsize=(8, 4.5))
plt.plot(
wti_df['date'],
wti_df['value'],
color=FL_BLUE,
linewidth=1.2,
label='WTI (DCOILWTICO)'
)
plt.plot(
brent_df['date'],
brent_df['value'],
color=FL_AMBER,
linewidth=1.2,
label='Brent (DCOILBRENTEU)'
)
plt.fill_between(
wti_df['date'],
wti_df.set_index('date').reindex(wti_df['date'])['value'],
brent_df.set_index('date').reindex(wti_df['date'])['value'],
alpha=0.08,
color=FL_SLATE,
label='WTI vs Brent spread'
)
plt.ylabel('Price ($/barrel)')
plt.title('Crude oil prices: WTI vs Brent')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y:.0f}')
)
plt.legend(fontsize=9)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(ng_df['date'], ng_df['value'], alpha=0.15, color=FL_GREEN)
plt.plot(ng_df['date'], ng_df['value'], color=FL_GREEN, linewidth=1.2)
plt.ylabel('Price ($/MMBtu)')
plt.title('Henry Hub natural gas spot price (DHHNGSP)')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y:.2f}')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
wti_recent = wti_df.tail(6).copy()
wti_recent['date'] = wti_recent['date'].dt.strftime('%Y-%m-%d')
wti_recent.columns = ['Date', 'WTI ($/bbl)']
wti_recent['WTI ($/bbl)'] = wti_recent['WTI ($/bbl)'].map(lambda v: f'${v:.2f}')
print('Recent WTI observations')
display(wti_recent)
Recent WTI observations
| Date | WTI ($/bbl) | |
|---|---|---|
| 2841 | 2026-05-11 | $101.56 |
| 2842 | 2026-05-12 | $105.78 |
| 2843 | 2026-05-13 | $104.52 |
| 2844 | 2026-05-14 | $104.66 |
| 2845 | 2026-05-15 | $108.99 |
| 2846 | 2026-05-18 | $112.25 |
Metals and agricultural commodities
FRED carries monthly global commodity price indices from the IMF Primary Commodity Prices database. Series IDs follow the pattern P<COMMODITY>USDM.
| Series ID | Description | Frequency |
|---|---|---|
PCOPPUSDM |
Global price of Copper ($/mt) | Monthly |
PALUMUSDM |
Global price of Aluminum ($/mt) | Monthly |
PWHEAMTUSDM |
Global price of Wheat ($/mt) | Monthly |
PMAIZMTUSDM |
Global price of Corn ($/mt) | Monthly |
PCOTTINDUSDM |
Global price of Cotton (cents/lb) | Monthly |
PSUGAISAUSDM |
Global price of Sugar No. 11 (cents/lb) | Monthly |
PCOFFOTMUSDM |
Global price of Coffee, Arabica (cents/lb) | Monthly |
PALLFNFINDEXQ |
Global Price Index of All Commodities | Quarterly |
copper_df, copper_info = fetch('PCOPPUSDM', start='2010-01-01')
alum_df, alum_info = fetch('PALUMUSDM', start='2010-01-01')
wheat_df, wheat_info = fetch('PWHEAMTUSDM', start='2010-01-01')
corn_df, corn_info = fetch('PMAIZMTUSDM', start='2010-01-01')
cotton_df, cotton_info = fetch('PCOTTINDUSDM', start='2010-01-01')
sugar_df, sugar_info = fetch('PSUGAISAUSDM', start='2010-01-01')
coffee_df, coffee_info = fetch('PCOFFOTMUSDM', start='2010-01-01')
allcom_df, allcom_info = fetch('PALLFNFINDEXQ', start='2010-01-01')
plt.figure(figsize=(8, 4.5))
plt.plot(
copper_df['date'],
copper_df['value'],
color=FL_AMBER,
linewidth=1.4,
label='Copper (PCOPPUSDM)'
)
plt.plot(
alum_df['date'],
alum_df['value'],
color=FL_SLATE,
linewidth=1.4,
label='Aluminum (PALUMUSDM)'
)
plt.ylabel('Price ($/mt)')
plt.title('Global metal prices: copper vs aluminum')
plt.gca().yaxis.set_major_formatter(
mticker.FuncFormatter(lambda y, _: f'${y:,.0f}')
)
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.legend(fontsize=9)
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(allcom_df['date'], allcom_df['value'], alpha=0.12, color=FL_BLUE)
plt.plot(allcom_df['date'], allcom_df['value'], color=FL_BLUE, linewidth=1.4)
plt.ylabel('Index')
plt.title('Global price index for all commodities (PALLFNFINDEXQ)')
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(wheat_df['date'], wheat_df['value'], alpha=0.12, color=FL_AMBER)
plt.plot(wheat_df['date'], wheat_df['value'], color=FL_AMBER, linewidth=1.3)
plt.title(wheat_info.title[:40] + ('...' if len(wheat_info.title) > 40 else ''))
plt.ylabel(wheat_info.units[:25])
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(corn_df['date'], corn_df['value'], alpha=0.12, color=FL_GREEN)
plt.plot(corn_df['date'], corn_df['value'], color=FL_GREEN, linewidth=1.3)
plt.title(corn_info.title[:40] + ('...' if len(corn_info.title) > 40 else ''))
plt.ylabel(corn_info.units[:25])
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8, 4.5))
plt.fill_between(coffee_df['date'], coffee_df['value'], alpha=0.12, color=FL_SLATE)
plt.plot(coffee_df['date'], coffee_df['value'], color=FL_SLATE, linewidth=1.3)
plt.title(coffee_info.title[:40] + ('...' if len(coffee_info.title) > 40 else ''))
plt.ylabel(coffee_info.units[:25])
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
plt.tick_params(length=0)
plt.tight_layout()
plt.show()
other_agri_df = pd.DataFrame([
{
'Series': cotton_info.name,
'Title': cotton_info.title,
'Units': cotton_info.units,
'Frequency': cotton_info.frequency,
'Last updated': str(cotton_info.last_updated)[:10],
'Latest value': round(float(cotton_df['value'].iloc[-1]), 4),
'Latest date': cotton_df['date'].iloc[-1].strftime('%Y-%m-%d'),
'Observations': len(cotton_df),
},
{
'Series': sugar_info.name,
'Title': sugar_info.title,
'Units': sugar_info.units,
'Frequency': sugar_info.frequency,
'Last updated': str(sugar_info.last_updated)[:10],
'Latest value': round(float(sugar_df['value'].iloc[-1]), 4),
'Latest date': sugar_df['date'].iloc[-1].strftime('%Y-%m-%d'),
'Observations': len(sugar_df),
}
])
print('Cotton and sugar series summary')
display(other_agri_df.T)
Cotton and sugar series summary
| 0 | 1 | |
|---|---|---|
| Series | None | None |
| Title | Global price of Cotton | Global price of Sugar, No. 11, World |
| Units | U.S. Cents per Pound | U.S. Cents per Pound |
| Frequency | Monthly | Monthly |
| Last updated | 2026-04-15 | 2026-04-15 |
| Latest value | 76.9386 | 14.7964 |
| Latest date | 2026-03-01 | 2026-03-01 |
| Observations | 195 | 195 |
Browsing the FRED category hierarchy
Every FRED series belongs to a category. The fred/category/children endpoint returns the sub-categories of any category ID. This is useful for discovering series - you can walk the tree from a root category down to specific series lists.
def fetch_subcategories(category_id):
r = requests.get(
f'{BASE_URL}/category/children',
params={
'category_id': category_id,
'api_key': FRED_API_KEY,
'file_type': 'json'
},
timeout=15
)
r.raise_for_status()
return r.json().get('categories', [])
def fetch_series_in_category(category_id, limit=10):
r = requests.get(
f'{BASE_URL}/category/series',
params={
'category_id': category_id,
'api_key': FRED_API_KEY,
'file_type': 'json',
'limit': limit,
'order_by': 'popularity',
'sort_order': 'desc'
},
timeout=15
)
r.raise_for_status()
return r.json().get('seriess', [])
mbf_subs = fetch_subcategories(32991)
mbf_subs_df = pd.DataFrame([
{
'Category ID': cat.get('id'),
'Category name': cat.get('name')
}
for cat in mbf_subs[:10]
])
print('Sub-categories of Money, Banking, and Finance (32991)')
display(mbf_subs_df)
rate_series = fetch_series_in_category(22, limit=10)
series_df = pd.DataFrame([
{
'ID': s['id'],
'Title': s['title'][:55] + ('...' if len(s['title']) > 55 else ''),
'Frequency': s['frequency_short'],
'Units': s['units_short'],
'Popularity': s['popularity'],
}
for s in rate_series
])
print('Top 10 series by popularity in Interest Rates (22)')
display(series_df)
Sub-categories of Money, Banking, and Finance (32991)
| Category ID | Category name | |
|---|---|---|
| 0 | 22 | Interest Rates |
| 1 | 15 | Exchange Rates |
| 2 | 24 | Monetary Data |
| 3 | 46 | Financial Indicators |
| 4 | 23 | Banking |
| 5 | 32360 | Business Lending |
| 6 | 32145 | Foreign Exchange Intervention |
Top 10 series by popularity in Interest Rates (22)