World Economic Outlook
The World Economic Outlook (WEO) is the IMF's database of macroeconomic projections and estimates, released every April and October. The weo module fetches a release as a single pandas DataFrame.
Fetch the latest release
Output:
Output:
UNIT_CODE CONCEPT_CODE REF_AREA_CODE REF_AREA_IMF_CODE FREQ_CODE LASTACTUALDATE SCALE_CODE NOTES TIME_PERIOD OBS_VALUE UNIT_LABEL CONCEPT_LABEL REF_AREA_LABEL FREQ_LABEL SCALE_LABEL COUNTRY_UPDATE_DATE
0 USD BCA ABW 314 A 2024 1000000000 <NA> 1999 -0.435363 US dollar Current account balance (credit less debit), US dollar Aruba, Kingdom of the Netherlands Annual Billions 2025-09-19
1 USD BCA ABW 314 A 2024 1000000000 <NA> 2000 0.212542 US dollar Current account balance (credit less debit), US dollar Aruba, Kingdom of the Netherlands Annual Billions 2025-09-19
2 USD BCA ABW 314 A 2024 1000000000 <NA> 2001 0.310076 US dollar Current account balance (credit less debit), US dollar Aruba, Kingdom of the Netherlands Annual Billions 2025-09-19
Fetch a specific release
The version argument is a (month, year) tuple, month first. The month must be "April" or "October", matching the IMF's twice-yearly release schedule. An invalid version raises NoDataError. See Errors below for where to import it from and what else fetch_data can raise.
List available releases
Output:
[('April', 2026), ('October', 2025), ('April', 2025), ('October', 2024), ('April', 2024), ('April', 2023), ('October', 2022), ('April', 2022), ('October', 2021), ('October', 2020), ('April', 2020), ('October', 2019), ('April', 2019)]
('April', 2021) and ('October', 2023) are absent. Both are corrupt in the IMF's own published archive, so this package can't serve them. See WEO coverage and known issues for what that means and how it surfaces.
Know which release you got
weo.fetch_data.last_version_fetched is a function attribute, set after each successful call, holding the version tuple that was returned.
Output:
Heads up
If the release you asked for has no data yet, fetch_data() rolls back: for version=None
("latest"), it walks get_weo_versions() newest-first and tries up to 3 older releases,
logging each attempt at WARNING. An explicit version (e.g. fetch_data(("October", 2025)))
never rolls back to a different release. If the API can't serve it, fetch_data falls back
to the bulk archive for that same version first (logging a warning), and only raises if the
archive can't serve it either. The version you asked for and the version you got can differ
only for version=None. Check last_version_fetched when the exact release matters.
Filter the frame
fetch_data() returns data in long format: one row per area, concept, and year.
from imf_reader import weo
df = weo.fetch_data()
growth = df[
(df.REF_AREA_CODE == "NGA")
& (df.CONCEPT_CODE == "NGDP_RPCH")
& (df.TIME_PERIOD.between(2020, 2024))
]
print(growth[["REF_AREA_LABEL", "TIME_PERIOD", "OBS_VALUE", "UNIT_LABEL"]].to_string(index=False))
Output:
REF_AREA_LABEL TIME_PERIOD OBS_VALUE UNIT_LABEL
Nigeria 2020 -6.368898 Percent
Nigeria 2021 1.109253 Percent
Nigeria 2022 4.318829 Percent
Nigeria 2023 3.315904 Percent
Nigeria 2024 4.071067 Percent
Find an indicator code
CONCEPT_CODE and CONCEPT_LABEL pair up one to one, so deriving the code list from a fetched frame finds the code for any indicator by name.
from imf_reader import weo
df = weo.fetch_data()
concepts = df[["CONCEPT_CODE", "CONCEPT_LABEL"]].drop_duplicates()
print(concepts.head().to_string(index=False))
Output:
CONCEPT_CODE CONCEPT_LABEL
BCA Current account balance (credit less debit), US dollar
BCA_NGDPD Current account balance (credit less debit), Percent of GDP
GGR Revenue, General government, Domestic currency
GGR_NGDP Revenue, General government, Percent of GDP
GGX Expenditure, General government, Domestic currency
The April 2026 release covers 145 concepts across 210 areas, with TIME_PERIOD spanning 1980 to 2031.
Columns
The 16 columns below are identical in name, order, and meaning on both source paths (the API and the bulk archive).
| Column | dtype | What it holds |
|---|---|---|
UNIT_CODE |
string | Unit code, e.g. USD |
CONCEPT_CODE |
string | Indicator code, e.g. NGDP_RPCH |
REF_AREA_CODE |
string | ISO3 (NGA) or G-prefixed aggregate (G001) |
REF_AREA_IMF_CODE |
Int64 | Legacy numeric IMF area code. Null where none exists. Removed in 3.0 |
FREQ_CODE |
string | Frequency code, e.g. A |
LASTACTUALDATE |
Int64 | Last year of actual (non-forecast) data. From LATEST_ACTUAL_ANNUAL_DATA on the API path (fiscal-year forms like FY2023/24 are collapsed to their leading year, 2023, a one-way, lossy mapping that discards the fiscal-year distinction. Read START_END_MONTHS_OF_REPORTING_YEAR from the IMF API directly for that detail), the XML series attribute of the same name on the bulk path |
SCALE_CODE |
Int64 | Multiplier, e.g. 1000000000 |
NOTES |
string | Observation notes. From METHODOLOGY_NOTES on the API path, the XML series attribute of the same name on the bulk path, different free text between the two paths with no column marking which one a given row came from. Split on the release version you requested if that matters |
TIME_PERIOD |
Int64 | Year |
OBS_VALUE |
Float64 | The value, expressed in SCALE_CODE units |
UNIT_LABEL |
string | e.g. US dollar |
CONCEPT_LABEL |
string | e.g. Current account balance (credit less debit), US dollar |
REF_AREA_LABEL |
string | e.g. Nigeria |
FREQ_LABEL |
string | e.g. Annual |
SCALE_LABEL |
string | Units, Millions, or Billions |
COUNTRY_UPDATE_DATE |
datetime64[us] | Date the country's data was last revised. From the API path's metadata sidecar. Always null on the bulk path (no per-country revision date in the XML) |
See WEO coverage and known issues for where these columns have gaps or quirks.
Series metadata
weo.fetch_series_metadata(version=None) fetches the IMF's series-level metadata sidecar, one row per series, covering methodology, classification, and reporting-convention detail that fetch_data() doesn't carry. imf_reader.weo.api.get_series_metadata(version=None) is the same call at the api layer.
Output:
Only three columns are guaranteed present release to release, REF_AREA_CODE, CONCEPT_CODE, and FREQ_CODE, the join keys below. The rest of the column set is release-dependent by design, since it follows whatever attribute set the IMF's DSD carries for that release, and that attribute set moves between releases.
Every column, including columns that look numeric such as BASE_YEAR and DECIMALS_DISPLAYED, is typed string. Native type inference for these columns changes from one release to the next. In BASE_YEAR, most values are a plain year like 2013, while some carry a fiscal-year form like FY2003/04 that native inference would either choke on or silently misread.
Values arrive as the IMF publishes them. A cell whose literal content is N/A stays the string N/A, distinct from an empty cell, which is null. 19 cells across METHODOLOGY_NOTES and BASIS_OF_PROJECTIONS take that form in the April 2026 release. fetch_data()'s NOTES column reads both forms as null, since null is the right value for "no note" there.
Merge onto observations
weo.fetch_data_with_metadata(version=None) returns fetch_data()'s observations left-merged with fetch_series_metadata()'s columns on REF_AREA_CODE, CONCEPT_CODE, and FREQ_CODE, one call for both:
Output:
Two independent version=None calls, one to fetch_data() and one to fetch_series_metadata(), can resolve to different releases if a new one is published between them, merging one release's metadata onto another release's observations with nothing to signal the mismatch. fetch_data_with_metadata() resolves both halves to the release its own observations call served, and pins them to that release's dataflow version. Doing the merge by hand needs the same pin, using fetch_data.last_version_fetched:
from imf_reader import weo
df = weo.fetch_data()
meta = weo.fetch_series_metadata(weo.fetch_data.last_version_fetched)
merged = df.merge(meta, on=["REF_AREA_CODE", "CONCEPT_CODE", "FREQ_CODE"], how="left")
Series metadata exists only for releases the API itself serves, April 2025 onward, with no bulk-archive fallback. A version the API can't serve raises VersionNotAvailableError. A frame of null columns would assert that the IMF publishes no methodology for these series, when this source carries no series metadata at all. See WEO coverage and known issues.
Excluded and always-null columns
Three sidecar columns are left off fetch_series_metadata()'s output. COUNTRY_UPDATE_DATE is excluded because fetch_data() already publishes it. UNIT and SCALE are excluded because they duplicate UNIT_CODE and SCALE_CODE. The sidecar's SCALE carries the bare exponent (e.g. 9), while fetch_data()'s SCALE_CODE carries the multiplier that exponent converts to (1000000000). Mixing the two up silently changes a value's magnitude by a power of ten.
Four columns are present but null across every one of the 8,200 series in the April 2026 release, FUNCTIONAL_CAT, COICOP_1999, TRANSFORMATION, and REPORTING_PERIOD_TYPE.
Columns holding more than one value
A handful of columns hold multiple ;-delimited values in one cell (their raw header carries a trailing [] marker from the IMF's CSV writer, which fetch_series_metadata() strips). Spacing around the delimiter isn't consistent. TOPIC holds values like F32;F32_CA with no space after the semicolon, while FISCAL_SECTOR_GENERAL_GOVERNMENT_COMPOSITION holds values like Central Government; Local Government; Social Security Funds with one. Split on ; and strip whitespace from each piece to get a clean list:
Series metadata columns
| Column | What it holds |
|---|---|
REF_AREA_CODE |
Join key. ISO3 or G-prefixed aggregate, matching fetch_data()'s column of the same name |
CONCEPT_CODE |
Join key. Indicator code, matching fetch_data()'s column of the same name |
FREQ_CODE |
Join key. Frequency code, matching fetch_data()'s column of the same name |
DECIMALS_DISPLAYED |
Number of decimal places the IMF displays for the series |
FUNCTIONAL_CAT |
Functional classification category. Null across all 8,200 series in the April 2026 release |
INT_ACC_ITEM |
Balance of payments item code (BPM6), e.g. CAB for current account balance |
NA_STO |
National accounts stock or transaction code (SNA), e.g. B1GQ for GDP |
GFS_STO |
Government Finance Statistics stock or transaction code |
COICOP_1999 |
Classification of Individual Consumption by Purpose (1999). Null across all 8,200 series in the April 2026 release |
TRADE_FLOW |
Trade flow direction, e.g. XG for exports of goods |
COMMODITY |
Commodity code for a commodity-price series |
SOC_CONCEPTS |
Social statistics concept code, e.g. POP for population |
SECTOR |
Institutional sector code (SNA), e.g. S13 for general government |
ACCOUNTING_ENTRY |
Accounting entry type, e.g. N for net |
INDEX_TYPE |
Index type, e.g. CPI |
PRICES |
Price basis, e.g. V for value or Q for volume |
STATISTICAL_MEASURES |
Statistical measure applied, e.g. RT for rate of change |
EXRATE |
Exchange rate type used to convert the series, e.g. XDC_PU |
TRANSFORMATION |
Data transformation applied to the series. Null across all 8,200 series in the April 2026 release |
REPORTING_PERIOD_TYPE |
Reporting period type. Null across all 8,200 series in the April 2026 release |
OVERLAP |
Overlap indicator between historical and projected data, e.g. OL |
TOPIC |
Subject area code(s), ;-delimited, e.g. F32;F32_CA |
METHODOLOGY |
Name of the statistical manual the series follows, e.g. Balance of Payments and International Investment Position Manual, sixth edition (BPM6) |
METHODOLOGY_NOTES |
Free-text methodology note. The same field fetch_data()'s NOTES column reads on API-served releases |
KEY_INDICATOR |
true where the IMF flags the series as one of its key indicators, otherwise null |
SERIES_NAME |
Human-readable series name |
LATEST_ACTUAL_ANNUAL_DATA |
Last year of actual (non-forecast) data, preserving fiscal-year forms such as FY2023/24. The field fetch_data()'s LASTACTUALDATE reads and collapses to its leading year |
HISTORICAL_DATA_SOURCE |
National institution supplying historical data, e.g. Central Bank |
BASE_YEAR |
Base year for index or volume calculations, e.g. 2013. Occasionally a fiscal-year form such as FY2003/04 |
START_END_MONTHS_OF_REPORTING_YEAR |
Start and end months of the fiscal reporting year, e.g. January/December |
CHAIN_WEIGHTED |
Whether the series uses chain-weighted volume measures, e.g. Yes, from 2000 |
BASIS_OF_PROJECTIONS |
Basis the IMF projects the series on, e.g. Government budget and projected nominal GDP |
VALUATION |
Valuation basis, e.g. Cash or Accrual |
PRICES_SECTOR_HARMONIZED_PRICES |
Whether the sector uses harmonised prices, Yes or No |
LABOR_SECTOR_EMPLOYMENT_TYPE |
Employment definition used for a labour-sector series, e.g. Harmonized ILO definition |
FISCAL_SECTOR_GENERAL_GOVERNMENT_COMPOSITION |
Subsectors composing "general government" for this series, ;-delimited, e.g. Central Government; Local Government; Social Security Funds |
FISCAL_SECTOR_VALUATION_OF_DEBT |
Valuation basis for government debt, e.g. Nominal value |
FISCAL_SECTOR_INSTRUMENTS_INCLUDED_IN_GROSS_AND_NET_DEBT |
Debt instruments included in gross and net debt figures, ;-delimited |
TRADE_SECTOR_OIL_COVERAGE |
Oil product coverage for a trade-sector series, ;-delimited |
PRIMARY_DOMESTIC_CURRENCY |
The area's primary domestic currency, e.g. Aruban Florin |
KEYWORDS |
Free-text search keywords, ;-delimited |
Many of the coded columns above hold a short code rather than a label, and that code is meant to be looked up, not read on its own. The lookup is the same IMF codelist API this package's own label columns are built from. A coded column's values key into a codelist named CL_<column name> under https://api.imf.org/external/sdmx/3.0/structure/codelist/IMF/. SECTOR, for instance, looks up against https://api.imf.org/external/sdmx/3.0/structure/codelist/IMF/CL_SECTOR.
Errors
All three are importable from imf_reader.config:
NoDataError, the base exception for "no WEO data could be resolved". Raised directly for an invalidversionargument, and is the parent class of the two below, so catching it covers every case.VersionNotAvailableError(NoDataError), raised when the requested version isn't served by the API. The bulk archive may still have it.DataflowDiscoveryError(NoDataError), raised when the IMF's dataflow catalogue responds but carries no usable WEO dataflow, so no release, including "latest", can be resolved from it.
All three import from imf_reader.config. imf_reader.cache re-exports BulkPayloadCorruptError
alone.
Clearing the cache
weo.clear_cache() works but is deprecated: it emits a DeprecationWarning and is removed in 3.0. Use cache.clear_cache(scope="weo") instead. See Caching for the full cache model.
Next steps
- WEO coverage and known issues - the permanent quirks in the underlying data and how this package handles them
- Caching - how WEO releases and version mappings are cached, and how to clear or relocate the cache
- Special Drawing Rights - the other IMF dataset this package reads, with its own date-order convention