L logiover
data · Jul 9, 2026 · 7 min read

How to Scrape WHO Global Health Observatory Data (2026)

Extract 3,000+ WHO health indicators by country and year — life expectancy, mortality, disease burden — from the official GHO OData API. No API key.

The WHO Global Health Observatory holds more than 3,000 health indicators — life expectancy, maternal and child mortality, disease burden, nutrition, tobacco, alcohol, health-system performance — across every country and decades of years. It’s all openly published under CC BY 4.0. The friction isn’t access; it’s that the official interface is an OData REST API, and if you just want “life expectancy by country and year in a spreadsheet,” you’re suddenly writing pagination loops and parsing dimension codes. This guide covers how to pull GHO data at scale: how the OData endpoint is structured, what a row looks like, and the quirks (null values, sex dimensions, indicator codes) that trip people up.

What’s worth extracting

Each data row carries 10 fields, straight from the GHO API:

  • Indicator identityindicatorCode (the WHO code, e.g. WHOSIS_000001) and indicatorName (the human-readable name, e.g. “Life expectancy at birth (years)”).
  • Geography & timecountry (ISO3 code, e.g. USA, GBR, DEU), year (e.g. 2019), and parentLocation (the WHO region name, e.g. “Europe”, “South-East Asia”, “Americas”).
  • The measurementvalue (the numeric value for that country/year/dimension), low and high (the lower and upper bounds of the 95% confidence interval), and rawValue (the original formatted string from WHO, e.g. "73.2 [72.8-73.6]").
  • The breakdowndimension, the primary dimension for the row, such as SEX_BTSX (both sexes), SEX_MLE (male) or SEX_FMLE (female).

For a trend chart you need country, year and value. For error bars you also want low and high. The dimension field is the one that catches people out — many indicators return multiple rows per country/year split by sex or age, so you filter on it downstream.

How the source actually works

The actor connects directly to WHO’s official GHO OData REST API at https://ghoapi.azureedge.net/api/. No authentication, no key, no cookies, no browser — the endpoint is fully public. It runs in two modes:

  • indicators — list every indicator code and name (roughly 3,000 rows) so you can discover what exists before fetching anything.
  • indicatorData — fetch every country/year data row for one or more indicator codes.

Pagination is handled automatically with a page size of 1,000 rows. That matters because coverage varies wildly by indicator: a high-coverage one like Life Expectancy (WHOSIS_000001) returns 12,000+ rows spanning 180+ countries, 30+ years and sex breakdowns, while a niche indicator returns a few hundred.

The normal workflow is discover-then-fetch. First list the indicators:

{
  "mode": "indicators",
  "maxResults": 0
}

Search the resulting names for keywords like “mortality”, “tuberculosis” or “obesity” to find the codes you want, then fetch their data. You can pass several codes at once and scope to specific countries:

{
  "mode": "indicatorData",
  "indicatorCodes": ["WHOSIS_000001", "WHOSIS_000002"],
  "countryFilter": ["USA", "GBR", "DEU", "FRA"],
  "maxResults": 0,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["DATACENTER"] }
}

maxResults: 0 means no limit (fetch everything); set a number like 500 for a quick preview. countryFilter takes ISO3 codes and defaults to empty (all countries). Multiple codes in indicatorCodes are processed sequentially and pushed into one dataset.

Run the WHO GHO Scraper on Apify — one run returns hundreds of thousands of health-indicator rows by country and year, with value, confidence intervals and WHO region. Keyless OData API, no login. Export to JSON, CSV, Excel or XML.

The access reality

The GHO API is one of the more permissive public data sources, so the friction is mostly about data shape rather than gatekeeping:

  • Fully keyless. No WHO account, no registration, no cookies. Datacenter proxy is sufficient — you don’t need residential IPs.
  • It’s generally permissive on rate. The actor retries failed requests up to three times with exponential back-off. Running many indicator codes in parallel is where you’d want a small delay between runs, but single-indicator pulls are fast — a 12,000-row indicator fetches in under two minutes.
  • Row counts vary by orders of magnitude. There’s no fixed “records per indicator.” Use the OData $count field on the raw API to estimate a large indicator before committing to a full run, or preview with maxResults: 500.
  • Some indicator codes are aliases. A code like Adult_curr_cig_smoking can redirect to another (TOBACCO_INDICATOR). If a fetch returns nothing, confirm the code exists by running indicators mode first rather than assuming the API is down.
  • You can’t filter by region in the input. countryFilter takes ISO3 country codes only. The WHO region lives in the output parentLocation field, so to work by region you fetch the countries and group on parentLocation downstream.

There’s no reason to scrape WHO’s dashboards or download CSVs by hand when the OData API returns clean, structured, paginated JSON. The work is purely turning that API into a tidy dataset.

Example output

One representative row:

{
  "indicatorCode": "WHOSIS_000001",
  "indicatorName": "Life expectancy at birth (years)",
  "country": "JPN",
  "year": "2021",
  "value": 84.26,
  "low": 83.91,
  "high": 84.61,
  "dimension": "SEX_BTSX",
  "parentLocation": "Western Pacific",
  "rawValue": "84.3 [83.9-84.6]"
}

Use cases

What structured WHO indicator data supports:

  • SDG & health-target reporting — track life expectancy by country and sex from 2000 to present for SDG-3 reporting, using value plus the low/high confidence bounds.
  • Regional comparison — compare maternal mortality ratios across WHO regions (grouping on parentLocation) to identify where interventions are most needed.
  • Multi-indicator dashboards — pull several indicator codes in one run to build, for example, an excess-mortality or disease-burden dashboard feeding Tableau, Power BI or Looker.
  • NGO grant proposals — export child-malnutrition or immunization data into Google Sheets to back funding proposals with authoritative figures.
  • Warehouse ingestion — load WHO indicators into BigQuery, Snowflake or Postgres as a reference dataset for downstream analytics.
  • AI health agents — ground an agent on current WHO figures so it answers, for instance, “which Sub-Saharan African countries improved life expectancy most from 2010 to 2023?” from real data.

The actor is MCP-compatible, so an agent can discover indicators and then fetch the exact rows it needs for reasoning, receiving paginated JSON ready for analysis.

Build-it-yourself vs. managed actor

The OData API is public, so a fetch loop is a legitimate build. What accumulates:

  • Pagination. OData pages in blocks; cycling through until the indicator is exhausted, per code, is the core loop you have to write and get right.
  • Dimension and null handling. Flattening the nested OData response into 10 clean fields, splitting out low/high from the raw string, and tolerating null numeric values requires per-field care.
  • Code discovery. You’ll still list indicators to find codes; the actor’s indicators mode does this in one call, but in a hand-rolled build it’s another endpoint to wire.
  • Retry & scheduling. Back-off on failures and recurring runs to keep the dataset fresh as WHO republishes estimates.

The managed actor is pay-per-result, so even a several-hundred-thousand-row multi-indicator pull is inexpensive. If you want the OData client in your own code, it’s a fine exercise. If you want WHO indicators as a clean dataset without writing the pagination and flattening, the managed path removes that layer.

Common pitfalls

Specific gotchas with WHO GHO data:

  • Multiple rows per country/year. Many indicators split by sex (SEX_MLE, SEX_FMLE, SEX_BTSX) or age group, so you’ll get several rows for the same country and year. If you’re charting a national trend, filter to SEX_BTSX or you’ll plot the same year three times.
  • Null values happen. Some country/year combinations have an aggregate text figure but no numeric breakdown — value, low or high come back null. Fall back to rawValue for the formatted string when the numeric fields are empty.
  • rawValue is for display, numeric fields for math. Use rawValue ("73.2 [72.8-73.6]") for human-readable labels; use value, low and high for any calculation. Don’t parse the raw string when the numeric columns already exist.
  • Alias codes return nothing. If a code silently yields zero rows, it may be an alias that redirects elsewhere. Verify against indicators mode before assuming the data is missing.
  • Region isn’t an input filter. You can’t request “Europe” directly — filter by ISO3 country in the input, then group on the output parentLocation. Trying to pass a region name as a country code returns nothing.
  • Coverage differs by indicator. Don’t assume every indicator spans all countries and years. A niche indicator may have a handful of rows; check with maxResults: 500 or the $count field before scaling up.

Wrapping up

The WHO GHO API is exactly the kind of open, well-behaved data source you want behind an extraction task — the work is turning its paginated OData responses into a tidy, analysis-ready dataset and handling the sex/age dimensions and null values. If you want the OData client in your stack, build it. If you just want life-expectancy, mortality or disease-burden data as clean rows for a dashboard, a warehouse or an AI agent, a managed actor that already does the discovery, pagination and flattening gets you there in one run.

Open the WHO GHO Scraper on Apify — queries the official WHO Global Health Observatory OData API in list-indicators or indicator-data mode and returns indicator code/name, country, year, value, confidence interval and WHO region per row. No API key, no login, MCP-ready. Export to JSON, CSV, Excel or XML. Start on Apify’s free monthly credit.

Related guides